在上文中,介绍的代理方案都是在代码运行时动态的生成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;}
编写代理配置类
@Aspectpublic 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 {//调用beforeAspectj.aspectOf().doBeforeTask2(var2);String result = "Hello World";System.out.println(result);var5 = result;} catch (Throwable var8) {Aspectj.aspectOf().after(var2);throw var8;}//调用afterAspectj.aspectOf().after(var2);var7 = var5;} catch (Throwable var9) {//调用抛出异常Aspectj.aspectOf().afterthrowing(var2);throw var9;}//调用returnAspectj.aspectOf().afterRutuen(var2);return (String)var7;}
很显然,代码被AspectJ编译器修改了,AspectJ并不是动态的在运行时生成代理类,而是在编译的时候就植入代码到class文件。由于是静态织入的,所以性能相对来说比较好!
