在上文中,介绍的代理方案都是在代码运行时动态的生成class文件达到动态代理的目的。
    回到问题的本质,其实动态代理的技术目的,主要为了解决静态代理模式中当目标接口发生了扩展,代理类也要跟着一遍变动的问题,避免造成了工作伤的繁琐和复杂。
    在 Java 生态里面,还有一个非常有名的第三方代理框架,那就是AspectJ,AspectJ通过特定的编译器可以将目标类编译成class字节码的时候,在方法周围加上业务逻辑,从而达到静态代理的效果。
    采用AspectJ进行方法植入,主要有四种:

    • 方法调用前拦截
    • 方法调用后拦截
    • 调用方法结束拦截
    • 抛出异常拦截

    使用起来也非常简单,首先是在项目中添加AspectJ编译器插件。

    1. <plugin>
    2. <groupId>org.codehaus.mojo</groupId>
    3. <artifactId>aspectj-maven-plugin</artifactId>
    4. <version>1.5</version>
    5. <executions>
    6. <execution>
    7. <goals>
    8. <goal>compile</goal>
    9. <goal>test-compile</goal>
    10. </goals>
    11. </execution>
    12. </executions>
    13. <configuration>
    14. <source>1.6</source>
    15. <target>1.6</target>
    16. <encoding>UTF-8</encoding>
    17. <complianceLevel>1.6</complianceLevel>
    18. <verbose>true</verbose>
    19. <showWeaveInfo>true</showWeaveInfo>
    20. </configuration>
    21. </plugin>

    然后,编写一个方法,准备进行代理。

    1. @RequestMapping({"/hello"})
    2. public String hello(String name) {
    3. String result = "Hello World";
    4. System.out.println(result);
    5. return result;
    6. }

    编写代理配置类

    1. @Aspect
    2. public class ControllerAspect {
    3. /***
    4. * 定义切入点
    5. */
    6. @Pointcut("execution(* com.example.demo.web..*.*(..))")
    7. public void methodAspect(){}
    8. /**
    9. * 方法调用前拦截
    10. */
    11. @Before("methodAspect()")
    12. public void before(){
    13. System.out.println("代理 -> 调用方法执行之前......");
    14. }
    15. /**
    16. * 方法调用后拦截
    17. */
    18. @After("methodAspect()")
    19. public void after(){
    20. System.out.println("代理 -> 调用方法执行之后......");
    21. }
    22. /**
    23. * 调用方法结束拦截
    24. */
    25. @AfterReturning("methodAspect()")
    26. public void afterReturning(){
    27. System.out.println("代理 -> 调用方法结束之后......");
    28. }
    29. /**
    30. * 抛出异常拦截
    31. */
    32. @AfterThrowing("methodAspect()")
    33. public void afterThrowing() {
    34. System.out.println("代理 -> 调用方法异常......");
    35. }
    36. }

    编译后,hello方法会变成这样。

    1. @RequestMapping({"/hello"})
    2. public String hello(Integer name) throws SQLException {
    3. JoinPoint var2 = Factory.makeJP(ajc$tjp_0, this, this, name);
    4. Object var7;
    5. try {
    6. Object var5;
    7. try {
    8. //调用before
    9. Aspectj.aspectOf().doBeforeTask2(var2);
    10. String result = "Hello World";
    11. System.out.println(result);
    12. var5 = result;
    13. } catch (Throwable var8) {
    14. Aspectj.aspectOf().after(var2);
    15. throw var8;
    16. }
    17. //调用after
    18. Aspectj.aspectOf().after(var2);
    19. var7 = var5;
    20. } catch (Throwable var9) {
    21. //调用抛出异常
    22. Aspectj.aspectOf().afterthrowing(var2);
    23. throw var9;
    24. }
    25. //调用return
    26. Aspectj.aspectOf().afterRutuen(var2);
    27. return (String)var7;
    28. }

    很显然,代码被AspectJ编译器修改了,AspectJ并不是动态的在运行时生成代理类,而是在编译的时候就植入代码到class文件。由于是静态织入的,所以性能相对来说比较好!