在上文中,介绍的代理方案都是在代码运行时动态的生成class文件达到动态代理的目的。
回到问题的本质,其实动态代理的技术目的,主要为了解决静态代理模式中当目标接口发生了扩展,代理类也要跟着一遍变动的问题,避免造成了工作伤的繁琐和复杂。
在 Java 生态里面,还有一个非常有名的第三方代理框架,那就是AspectJ,AspectJ通过特定的编译器可以将目标类编译成class字节码的时候,在方法周围加上业务逻辑,从而达到静态代理的效果。
采用AspectJ进行方法植入,主要有四种:
- 方法调用前拦截
- 方法调用后拦截
- 调用方法结束拦截
- 抛出异常拦截
使用起来也非常简单,首先是在项目中添加AspectJ编译器插件。
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<complianceLevel>1.6</complianceLevel>
<verbose>true</verbose>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
</plugin>
然后,编写一个方法,准备进行代理。
@RequestMapping({"/hello"})
public String hello(String name) {
String result = "Hello World";
System.out.println(result);
return result;
}
编写代理配置类
@Aspect
public class ControllerAspect {
/***
* 定义切入点
*/
@Pointcut("execution(* com.example.demo.web..*.*(..))")
public void methodAspect(){}
/**
* 方法调用前拦截
*/
@Before("methodAspect()")
public void before(){
System.out.println("代理 -> 调用方法执行之前......");
}
/**
* 方法调用后拦截
*/
@After("methodAspect()")
public void after(){
System.out.println("代理 -> 调用方法执行之后......");
}
/**
* 调用方法结束拦截
*/
@AfterReturning("methodAspect()")
public void afterReturning(){
System.out.println("代理 -> 调用方法结束之后......");
}
/**
* 抛出异常拦截
*/
@AfterThrowing("methodAspect()")
public void afterThrowing() {
System.out.println("代理 -> 调用方法异常......");
}
}
编译后,hello方法会变成这样。
@RequestMapping({"/hello"})
public String hello(Integer name) throws SQLException {
JoinPoint var2 = Factory.makeJP(ajc$tjp_0, this, this, name);
Object var7;
try {
Object var5;
try {
//调用before
Aspectj.aspectOf().doBeforeTask2(var2);
String result = "Hello World";
System.out.println(result);
var5 = result;
} catch (Throwable var8) {
Aspectj.aspectOf().after(var2);
throw var8;
}
//调用after
Aspectj.aspectOf().after(var2);
var7 = var5;
} catch (Throwable var9) {
//调用抛出异常
Aspectj.aspectOf().afterthrowing(var2);
throw var9;
}
//调用return
Aspectj.aspectOf().afterRutuen(var2);
return (String)var7;
}
很显然,代码被AspectJ编译器修改了,AspectJ并不是动态的在运行时生成代理类,而是在编译的时候就植入代码到class文件。由于是静态织入的,所以性能相对来说比较好!