• Spring原理学习(八)AOP底层实现


    一、Advice、Advisor、Advised接口

    Advice: org.aopalliance.aop.Advice
    “通知”,实际增强的逻辑部分。

    Pointcut: org.springframework.aop.Pointcut
    “切点”,一些特使的连接点,是具体附加通知的地方。例如坐地铁的时候,具体在某个站下车,那这个站就是切入点。

    • 连接点:在应用执行过程中能够插入切面(Aspect)的一个点。例如坐地铁的时候,每一个站都可以下车,那么这每一个站都是一个接入点。假如一个对象中有多个方法,那么这个每一个方法就是一个连接点。

    Advisor: org.springframework.aop.Advisor
    “通知者”,它持有 Advice,是 Spring AOP 的一个基础接口。

    Advised: org.springframework.aop.framework.Advised
    AOP 代理工厂配置类接口。提供了操作和管理 Advice 和 Advisor 的能力。

    1、Advice、Advisor、Advised 类图

    1. Advisor 可以获取到 Advice。

    2. PointcutAdvisor 可以获取到 Pointcut 和 Advice。
    Pointcut 可以匹配连接点,Advice 是具体的通知,所以,PointcutAdvisor 是一个功能完善接口。

    3. Advised 是 AOP 代理工厂配置类接口,它可以操作和管理 Advice 和 Advisor,它的实现类有 ProxyFactoryAspectJProxyFactory,用于生成AOP 代理类。

    2、Advice

     Advice 大体上分为了三类:BeforeAdvice、MethodInterceptor、AfterAdvice

    可以看出,MethodInterceptor 是功能最强大的,它能够处理 BeforeAdvice、AroundAdvice、AfterAdvice、ThrowsAdvice、@Valid方法参数校验、@Async异步等

    3、Advisor

    Advisor 大体分为了三类:PointcutAdvisor、IntroductionAdvisor、PrototypePlaceholderAdvisor
    其中,用到的最多的就是 PointcutAdvisor,它涵盖了绝大部分的 Advisor。 

    PointcutAdvisor

    PointcutAdvisor 是一个功能完善接口,也是 Spring AOP 中使用最多的,它涵盖了绝大部分的 Advisor。

    通过 PointcutAdvisor 可以获取到 Pointcut 和 Advice。Pointcut 可以完成连接点的匹配,而 Advice 就是在连接点上具体要执行的"通知"。

    4、Advised

     

    Advised 是 AOP 代理工厂配置类接口。

    它的实现类有:ProxyFactory、AspectJProxyFactory、ProxyFactoryBean。
    Advised 提供了操作和管理 Advice 和 Advisor 的能力,所以,ProxyFactory 实现 Advised 之后,就可以方便的获取和操作 Advice、Advisor,从而创建 AOP 代理类了。

    Advised、ProxyConfig、AdvisedSupport 都是跟 Spring AOP 代理配置相关的接口和类,它们可以统一 Spring AOP 的代理配置。

    参考文章:【Spring源码三千问】Advice、Advisor、Advised都是什么接口?

    二、Spring的代理选择规则

    代理相关类图

    • AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现

    • AopProxy 通过 getProxy 创建代理对象

    • 图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得)

    • 调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor

    先说结论: 

    • proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
    • proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
    • proxyTargetClass = true, 总是使用 cglib 实现
    1. public class A15 {
    2. public static void main(String[] args) {
    3. // 1. 备好切点
    4. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    5. pointcut.setExpression("execution(* foo())");
    6. // 2. 备好通知 ,注意同名MethodInterceptor
    7. MethodInterceptor advice = new MethodInterceptor() {
    8. @Override
    9. public Object invoke(MethodInvocation invocation) throws Throwable {
    10. System.out.println("before...");
    11. //调用目标
    12. Object result = invocation.proceed();
    13. System.out.println("after...");
    14. return result;
    15. }
    16. };
    17. // 3. 备好切面
    18. DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
    19. //4. 创建代理
    20. Target1 target1 = new Target1();
    21. ProxyFactory factory = new ProxyFactory();
    22. factory.setTarget(target1);
    23. factory.addAdvisor(advisor);
    24. I1 proxy = (I1) factory.getProxy();
    25. //5. 调用增强后的方法
    26. System.out.println(proxy.getClass());
    27. proxy.foo();
    28. proxy.bar();
    29. }
    30. interface I1 {
    31. void foo();
    32. void bar();
    33. }
    34. static class Target1 implements I1 {
    35. public void foo() {
    36. System.out.println("target1 foo");
    37. }
    38. public void bar() {
    39. System.out.println("target1 bar");
    40. }
    41. }
    42. static class Target2 {
    43. public void foo() {
    44. System.out.println("target2 foo");
    45. }
    46. public void bar() {
    47. System.out.println("target2 bar");
    48. }
    49. }
    50. }

     结果:使用 CGLIB 实现,不应该是 JDK 吗?

    1. class com.itheima.a15.A15$Target1$$EnhancerBySpringCGLIB$$88a7fb77
    2. before...
    3. target1 foo
    4. after...
    5. target1 bar

    解释:因为生成代理类时并不知道目标类是否实现了接口,需要告诉工厂目标类实现的接口

    factory.setInterfaces(target1.getClass().getInterfaces());

    设置接口后再进行测试,使用JDK实现

    class com.itheima.a15.$Proxy2

    设置 ProxyTargetClass 为 true 再进行测试,使用CGLIB实现

    factory.setProxyTargetClass(true);
    class com.itheima.a15.A15$Target1$$EnhancerBySpringCGLIB$$abb40a5f

    总结

    ProxyFactory 是用来创建代理的核心实现,用 AopProxyFactory 选择具体代理实现

    • JdkDynamicAopProxy
    • ObjenesisCglibAopProxy

    注意:要区分上面提到的 MethodInterceptor,它与之前 CGLIB 中用的的 MethodInterceptor 是不同的接口

    三、切点匹配

    1、根据指定的方法进行匹配

    1. public class A16 {
    2. public static void main(String[] args) throws NoSuchMethodException {
    3. AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
    4. //根据指定的方法进行匹配
    5. pt1.setExpression("execution(* bar())");
    6. //判断是否匹配
    7. System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
    8. System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));
    9. }
    10. static class T1 {
    11. public void foo() {
    12. }
    13. public void bar() {
    14. }
    15. }
    16. }

    matches方法的第二个参数可以随意指定,对于结果没有影响,后面在自己实现切点匹配时会解释。 

    结果:

    1. false
    2. true

    2、根据方法上的注解进行匹配

    1. public class A16 {
    2. public static void main(String[] args) throws NoSuchMethodException {
    3. AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
    4. //根据方法上的注解进行匹配
    5. pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
    6. System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
    7. System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));
    8. }
    9. static class T1 {
    10. @Transactional
    11. public void foo() {
    12. }
    13. public void bar() {
    14. }
    15. }
    16. }

    结果:

    1. true
    2. false

    3、自己实现切点匹配

    MergedAnnotations是一个注解的工具类

    1. public class A16 {
    2. public static void main(String[] args) throws NoSuchMethodException {
    3. StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
    4. @Override
    5. public boolean matches(Method method, Class targetClass) {
    6. /* 检查方法上是否加了 Transactional 注解 */
    7. //获取方法上标注的注解
    8. MergedAnnotations annotations = MergedAnnotations.from(method);
    9. //是否有@Transactional注解
    10. if (annotations.isPresent(Transactional.class)) {
    11. return true;
    12. }
    13. /* 查看类上是否加了 Transactional 注解 */
    14. //获取类上标注的注解
    15. annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
    16. //是否有@Transactional注解
    17. if (annotations.isPresent(Transactional.class)) {
    18. return true;
    19. }
    20. return false;
    21. }
    22. };
    23. System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
    24. System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
    25. System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
    26. System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));
    27. }
    28. static class T1 {
    29. @Transactional
    30. public void foo() {
    31. }
    32. public void bar() {
    33. }
    34. }
    35. @Transactional
    36. static class T2 {
    37. public void foo() {
    38. }
    39. }
    40. @Transactional
    41. interface I3 {
    42. void foo();
    43. }
    44. static class T3 implements I3 {
    45. public void foo() {
    46. }
    47. }
    48. }

     说明:from方法默认的搜索策略是在本类,那么如果在接口或者父类上标注 @Transactional 注解则查找不到。

    1. static MergedAnnotations from(AnnotatedElement element) {
    2. return from(element, MergedAnnotations.SearchStrategy.DIRECT);
    3. }

    所以更换了策略 

    MergedAnnotations.SearchStrategy.TYPE_HIERARCHY:从继承树查找

    默认策略的结果:

    1. true
    2. false
    3. true
    4. false

    指定为 TYPE_HIERARCHY 策略的结果:

    1. true
    2. false
    3. true
    4. true

    总结

    • 底层切点实现是如何匹配的:调用了 aspectj 的匹配方法,比较关键的是它实现了 MethodMatcher 接口,用来执行方法的匹配。
    • aspectj 切点的局限性:只能匹配方法的信息

    四、从 @Aspect 到 Advisor

    两个切面概念

    aspect =

    • 通知1(advice) + 切点1(pointcut)
    • 通知2(advice) + 切点2(pointcut)
    • 通知3(advice) + 切点3(pointcut)
    • ...

    advisor =

    • 更细粒度的切面,包含一个通知和切点

    aspect:高级切面,advisor:低级切面

    1、代理创建器

    AnnotationAwareAspectJAutoProxyCreator其实是个Bean后处理器,具体可以看它的继承关系。

    AnnotationAwareAspectJAutoProxyCreator的作用:

    • 将高级 @Aspect 切面统一为低级 Advisor 切面

    • 在合适的时机创建代理

    1. public class A17 {
    2. public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    3. GenericApplicationContext context = new GenericApplicationContext();
    4. context.registerBean("aspect1", Aspect1.class);
    5. context.registerBean("config", Config.class);
    6. context.registerBean(ConfigurationClassPostProcessor.class);
    7. //解析@Aspect,产生代理
    8. context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
    9. context.refresh();
    10. /*
    11. 第一个重要方法 findEligibleAdvisors:找到有资格的Advisors
    12. a. 有资格的 Advisor 一部分是低级的,可以由自己编写,如下例中的 advisor3
    13. b. 有资格的 Advisor 一部分是高级的,由解析 @Aspect 后获得
    14. */
    15. AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
    16. Method findEligibleAdvisors = creator.getClass().getSuperclass().getSuperclass().
    17. getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
    18. findEligibleAdvisors.setAccessible(true);
    19. //找到和 Target1 类匹配的所有切面
    20. // 参数一:目标类型,参数二:bean名字,由于没交给spring管理,随便写一个
    21. List advisors = (List) findEligibleAdvisors.invoke(creator, Target1.class, "target1");
    22. for (Advisor advisor : advisors) {
    23. System.out.println(advisor);
    24. }
    25. System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
    26. /*
    27. 第二个重要方法 wrapIfNecessary
    28. a. 它内部调用 findEligibleAdvisors,只要返回集合不为空,则表示需要创建代理
    29. */
    30. //参数一:目标对象 参数二:bean名字,由于没交给spring管理,随便写一个 参数三:对于这个操作没用,随便写一个
    31. Method wrapIfNecessary = creator.getClass().getSuperclass().getSuperclass().getSuperclass().
    32. getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
    33. wrapIfNecessary.setAccessible(true);
    34. Object o1 = wrapIfNecessary.invoke(creator, new Target1(), "target1", "target1");
    35. //代理对象
    36. System.out.println(o1.getClass());
    37. Object o2 = wrapIfNecessary.invoke(creator, new Target2(), "target2", "target2");
    38. //对象本身
    39. System.out.println(o2.getClass());
    40. ((Target1) o1).foo();
    41. }
    42. static class Target1 {
    43. public void foo(){
    44. System.out.println("target1 foo");
    45. }
    46. }
    47. static class Target2 {
    48. public void bar(){
    49. System.out.println("target2 bar");
    50. }
    51. }
    52. @Aspect //高级切面类
    53. static class Aspect1 {
    54. @Before("execution(* foo())")
    55. public void before(){
    56. System.out.println("aspect1 before...");
    57. }
    58. @After("execution(* foo())")
    59. public void after(){
    60. System.out.println("aspect1 after...");
    61. }
    62. }
    63. @Configuration
    64. static class Config {
    65. @Bean//低级切面
    66. public Advisor advisor3(MethodInterceptor advice3){
    67. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    68. pointcut.setExpression("execution(* foo())");
    69. DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
    70. return advisor;
    71. }
    72. @Bean
    73. public MethodInterceptor advice3() {
    74. return new MethodInterceptor() {
    75. @Override
    76. public Object invoke(MethodInvocation invocation) throws Throwable {
    77. System.out.println("advice3 before...");
    78. Object result = invocation.proceed();
    79. System.out.println("advice3 after...");
    80. return result;
    81. }
    82. };
    83. }
    84. }
    85. }

    结果:

    1. //spring加的,暴露调用器的拦截器,作用后面会讲
    2. org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
    3. //低级切面
    4. org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [com.itheima.a17.A17$Config$1@209da20d]
    5. //高级切面转换后的低级切面
    6. InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.itheima.a17.A17$Aspect1.before()]; perClauseKind=SINGLETON
    7. InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.itheima.a17.A17$Aspect1.after()]; perClauseKind=SINGLETON
    8. >>>>>>>>>>>>>>>>>>>>>>>>>>
    9. [TRACE] 16:10:29.788 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'target1' with 0 common interceptors and 4 specific interceptors
    10. class com.itheima.a17.A17$Target1$$EnhancerBySpringCGLIB$$a2f64ee5
    11. class com.itheima.a17.A17$Target2
    12. advice3 before...
    13. aspect1 before...
    14. target1 foo
    15. aspect1 after...
    16. advice3 after...

    总结

    1. AnnotationAwareAspectJAutoProxyCreator 的作用

      • 将高级 @Aspect 切面统一为低级 Advisor 切面

      • 在合适的时机创建代理

    2. findEligibleAdvisors 找到有【资格】的 Advisors

      • 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如本例 A17 中的 advisor3

      • 有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得

    3. wrapIfNecessary

      • 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理

      • 它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行

    2、代理创建时机

    1)代理的创建时机:

    • 初始化之后 (无循环依赖时)

    • 实例创建后,依赖注入前 (有循环依赖时),并暂存于二级缓存

    注意:不会重复创建代理,两个位置二选一

    无循环依赖

    1. public class A17_1 {
    2. public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    3. GenericApplicationContext context = new GenericApplicationContext();
    4. context.registerBean(Config.class);
    5. context.registerBean(ConfigurationClassPostProcessor.class);
    6. context.refresh();
    7. }
    8. @Configuration
    9. static class Config {
    10. @Bean //解析 @Aspect,产生代理
    11. public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
    12. return new AnnotationAwareAspectJAutoProxyCreator();
    13. }
    14. @Bean //解析 @Autowired
    15. public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
    16. return new AutowiredAnnotationBeanPostProcessor();
    17. }
    18. @Bean //解析 @PostConstruct
    19. public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
    20. return new CommonAnnotationBeanPostProcessor();
    21. }
    22. @Bean
    23. public Advisor advisor(MethodInterceptor advice) {
    24. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    25. pointcut.setExpression("execution(* foo())");
    26. return new DefaultPointcutAdvisor(pointcut, advice);
    27. }
    28. @Bean
    29. public MethodInterceptor advice(){
    30. return new MethodInterceptor() {
    31. @Override
    32. public Object invoke(MethodInvocation invocation) throws Throwable {
    33. System.out.println("before...");
    34. Object result = invocation.proceed();
    35. return result;
    36. }
    37. };
    38. }
    39. @Bean
    40. public Bean1 bean1() {
    41. return new Bean1();
    42. }
    43. @Bean
    44. public Bean2 bean2() {
    45. return new Bean2();
    46. }
    47. }
    48. static class Bean1 {
    49. public void foo(){
    50. }
    51. public Bean1(){
    52. System.out.println("Bean1()");
    53. }
    54. @PostConstruct
    55. public void init() {
    56. System.out.println("Bean1 init()");
    57. }
    58. }
    59. static class Bean2 {
    60. public Bean2(){
    61. System.out.println("Bean2()");
    62. }
    63. @Autowired
    64. public void setBean1(Bean1 bean1) {
    65. System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
    66. }
    67. @PostConstruct
    68. public void init() {
    69. System.out.println("Bean2 init()");
    70. }
    71. }
    72. }

    结果:

    1. Bean1()
    2. Bean1 init()
    3. [TRACE] 16:21:47.952 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
    4. Bean2()
    5. Bean2 setBean1(bean1) class is: class com.itheima.a17.A17_1$Bean1$$EnhancerBySpringCGLIB$$b0011e5d
    6. Bean2 init()

    有循环依赖时

    1. static class Bean1 {
    2. public void foo(){
    3. }
    4. public Bean1(){
    5. System.out.println("Bean1()");
    6. }
    7. @Autowired
    8. public void setBean1(Bean2 bean2) {
    9. System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
    10. }
    11. @PostConstruct
    12. public void init() {
    13. System.out.println("Bean1 init()");
    14. }
    15. }
    16. static class Bean2 {
    17. public Bean2(){
    18. System.out.println("Bean2()");
    19. }
    20. @Autowired
    21. public void setBean1(Bean1 bean1) {
    22. System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
    23. }
    24. @PostConstruct
    25. public void init() {
    26. System.out.println("Bean2 init()");
    27. }
    28. }

    结果:

    1. Bean1()
    2. Bean2()
    3. [TRACE] 16:23:35.276 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
    4. Bean2 setBean1(bean1) class is: class com.itheima.a17.A17_1$Bean1$$EnhancerBySpringCGLIB$$2d7cace9
    5. Bean2 init()
    6. Bean1 setBean2(bean2) class is: class com.itheima.a17.A17_1$Bean2
    7. Bean1 init()

    3、@Before 对应的低级通知

    @Before 前置通知会被转换为原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息

    1. 通知代码从哪儿来

    2. 切点是什么(这里为啥要切点, 后面解释)

    3.  Aspect 对象,将来反射调用切面类的方法需要切面类对象

    类似的还有:

    1. AspectJAroundAdvice (环绕通知)

    2. AspectJAfterReturningAdvice

    3. AspectJAfterThrowingAdvice (环绕通知)

    4. AspectJAfterAdvice (环绕通知)

    环绕通知:实现了 MethodInterceptor 接口的类

    1. public class A17_2 {
    2. static class Aspect {
    3. @Before("execution(* foo())")
    4. public void before1() {
    5. System.out.println("before1");
    6. }
    7. @Before("execution(* foo())")
    8. public void before2() {
    9. System.out.println("before2");
    10. }
    11. public void after() {
    12. System.out.println("after");
    13. }
    14. }
    15. static class Target {
    16. public void foo() {
    17. System.out.println("target foo");
    18. }
    19. }
    20. public static void main(String[] args) {
    21. //切面对象工厂,将来由工厂拿到切面对象
    22. AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
    23. List list = new ArrayList<>();
    24. //高级切面转低级切面类
    25. for (Method method : Aspect.class.getDeclaredMethods()) {
    26. //方法上是否标注 @Before 注解
    27. if (method.isAnnotationPresent(Before.class)) {
    28. //解析切点
    29. String expression = method.getAnnotation(Before.class).value();
    30. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    31. pointcut.setExpression(expression);
    32. //通知类
    33. //参数三:将来反射调用切面类的方法需要切面类对象,所以需要传入切面类对象,具体从工厂获取
    34. AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
    35. //切面
    36. DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
    37. list.add(advisor);
    38. }
    39. }
    40. for (Advisor advisor : list) {
    41. System.out.println(advisor);
    42. }
    43. }
    44. }

    结果:

    1. org.springframework.aop.support.DefaultPointcutAdvisor:
    2. pointcut [AspectJExpressionPointcut: () execution(* foo())];
    3. advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void com.itheima.a17.A17_2$Aspect.before1()]; aspect name '']
    4. org.springframework.aop.support.DefaultPointcutAdvisor:
    5. pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice:
    6. advice method [public void com.itheima.a17.A17_2$Aspect.before2()]; aspect name '']

    五、静态通知调用

    步骤:

    1. 高级切面转低级切面类
    2. 通知统一转换为环绕通知 MethodInterceptor
    3. 创建并执行调用链 (所有的环绕通知 + 目标)

    1、通知为什么需要统一转换为环绕通知?

    其实无论proxyFactory基于哪种方式创建,最后干活(调用 advice)的是一个 MethodInvocation 对象

    1)因为 advisor 有多个,且一个套一个调用,因此需要一个调用链对象,即 MethodInvocation

    2)MethodInvocation 要知道 advice 有哪些,还要知道目标,调用次序

    3)从上图可以看出,环绕通知才适合作为 advice,因此其他 before,afterReturning 都会被转换为环绕通知

    如何统一转换为 MethodInterceptor 环绕通知?

    统一转换为环绕通知,体现的是设计模式的适配器模式

    • 对外是为了方便使用要区分 before, afterReturning, afterThrowing
    • 对内统一都是环绕通知,统一用 MethodInterceptor 表示

    适配如下

    • MethodBeforeAdviceAdapter 将 AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
    • AfterReturningAdviceAdapter 将 AspectJAfterReturningAdvice 适配为 MethodAfterReturningAdviceInterceptor
    1. class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    2. MethodBeforeAdviceAdapter() {
    3. }
    4. //支持哪种通知的适配
    5. public boolean supportsAdvice(Advice advice) {
    6. return advice instanceof MethodBeforeAdvice;
    7. }
    8. //转换成 MethodInterceptor 接口
    9. public MethodInterceptor getInterceptor(Advisor advisor) {
    10. MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
    11. return new MethodBeforeAdviceInterceptor(advice);
    12. }
    13. }

    2、模拟静态通知调用

    1. public class A18 {
    2. static class Aspect {
    3. @Before("execution(* foo())")
    4. public void before1() {
    5. System.out.println("before1");
    6. }
    7. @Before("execution(* foo())")
    8. public void before2() {
    9. System.out.println("before2");
    10. }
    11. public void after() {
    12. System.out.println("after");
    13. }
    14. @AfterReturning("execution(* foo())")
    15. public void afterReturning() {
    16. System.out.println("afterReturning");
    17. }
    18. @AfterThrowing("execution(* foo())")
    19. public void afterThrowing(Exception e) {
    20. System.out.println("afterThrowing" + e.getMessage());
    21. }
    22. @Around("execution(* foo())")
    23. public Object around(ProceedingJoinPoint pjp) throws Throwable{
    24. System.out.println("around before...");
    25. Object result = pjp.proceed();
    26. System.out.println("around after...");
    27. return result;
    28. }
    29. }
    30. static class Target {
    31. public void foo() {
    32. System.out.println("target foo");
    33. }
    34. }
    35. public static void main(String[] args) throws Throwable {
    36. /* 1. 高级切面转低级切面类 */
    37. //切面对象工厂,由工厂拿到切面对象
    38. AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
    39. List list = new ArrayList<>();
    40. for (Method method : Aspect.class.getDeclaredMethods()) {
    41. //方法上是否标注 @Before 注解
    42. if (method.isAnnotationPresent(Before.class)) {
    43. //解析切点
    44. String expression = method.getAnnotation(Before.class).value();
    45. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    46. pointcut.setExpression(expression);
    47. //通知类
    48. AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
    49. //切面
    50. DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
    51. list.add(advisor);
    52. } else if (method.isAnnotationPresent(AfterReturning.class)) {
    53. //方法上是否标注 @AfterReturning 注解
    54. if (method.isAnnotationPresent(AfterReturning.class)) {
    55. //解析切点
    56. String expression = method.getAnnotation(AfterReturning.class).value();
    57. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    58. pointcut.setExpression(expression);
    59. //通知类
    60. AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
    61. //切面
    62. DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
    63. list.add(advisor);
    64. }
    65. } else if (method.isAnnotationPresent(Around.class)) {
    66. //方法上是否标注 @Around 注解
    67. if (method.isAnnotationPresent(Around.class)) {
    68. //解析切点
    69. String expression = method.getAnnotation(Around.class).value();
    70. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    71. pointcut.setExpression(expression);
    72. //通知类
    73. AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
    74. //切面
    75. DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
    76. list.add(advisor);
    77. }
    78. }
    79. }
    80. for (Advisor advisor : list) {
    81. System.out.println(advisor);
    82. }
    83. System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
    84. /* 省略了 advisor 排序步骤 */
    85. /* 2. 通知统一转换为环绕通知 MethodInterceptor */
    86. ProxyFactory proxyFactory = new ProxyFactory();
    87. Target target = new Target();
    88. proxyFactory.setTarget(target);
    89. //准备把 MethodInvocation 放入当前通知
    90. proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
    91. proxyFactory.addAdvisors(list);
    92. //把除了环绕通知以外的其他类型通知统一转换为环绕通知
    93. List methodInterceptorList =
    94. proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
    95. for (Object o : methodInterceptorList) {
    96. System.out.println(o);
    97. }
    98. /* 3. 创建并执行调用链 (所有的环绕通知 + 目标) */
    99. Constructor constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(
    100. Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
    101. constructor.setAccessible(true);
    102. ReflectiveMethodInvocation methodInvocation = constructor.newInstance(
    103. null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList);
    104. methodInvocation.proceed();
    105. /*
    106. 此步模拟调用链过程,是一个简单的递归调用
    107. 1. proceed() 方法调用链中下一个环绕通知
    108. 2. 每个环绕通知内部继续调用 proceed()
    109. 3. 调用到没有更多通知了,就调用目标方法
    110. */
    111. }
    112. }
    113. 结果:

      1. org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object com.itheima.a18.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
      2. org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void com.itheima.a18.A18$Aspect.afterReturning()]; aspect name '']
      3. org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void com.itheima.a18.A18$Aspect.before1()]; aspect name '']
      4. org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void com.itheima.a18.A18$Aspect.before2()]; aspect name '']
      5. >>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      6. org.springframework.aop.interceptor.ExposeInvocationInterceptor@8e24743
      7. org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object com.itheima.a18.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''
      8. org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@74a10858
      9. org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@23fe1d71
      10. org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@28ac3dc3
      11. around before...
      12. before1
      13. before2
      14. target foo
      15. afterReturning
      16. around after...

      ExposeInvocationInterceptor的作用

      某些通知会用到 MethodInvocation,在执行的时候,需要放在公共的位置,让所有的通知都能找到,如果某个通知想取的时候没取到,就会报如下错误:

      如何放置?

      ExposeInvocationInterceptor的作用就是将 MethodInvocation 放到当前线程,这样所有通知就都能获取到了,它同时也是个环绕通知,那把它放到最外层的环绕通知就可以了。

      总结 

      代理方法执行时会做如下工作

      1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知。这体现的是适配器设计模式

      2. 所谓静态通知,体现在上面方法的 Interceptors 部分,这些通知调用时无需再次检查切点,直接调用即可

      3. 结合目标与环绕通知链,创建 MethodInvocation 对象,通过它完成整个调用

      3、模拟 MethodInvocation

      用到的是职责链模式

      1. public class A18_1 {
      2. static class Target {
      3. public void foo() {
      4. System.out.println("Target.foo()");
      5. }
      6. }
      7. static class Advice1 implements MethodInterceptor {
      8. @Override
      9. public Object invoke(MethodInvocation invocation) throws Throwable {
      10. System.out.println("Advice1.before()");
      11. Object result = invocation.proceed(); //调用下一个通知目标
      12. System.out.println("Advice1.after()");
      13. return result;
      14. }
      15. }
      16. static class Advice2 implements MethodInterceptor {
      17. @Override
      18. public Object invoke(MethodInvocation invocation) throws Throwable {
      19. System.out.println("Advice2.before()");
      20. Object result = invocation.proceed(); //调用下一个通知目标
      21. System.out.println("Advice2.after()");
      22. return result;
      23. }
      24. }
      25. static class MyInvocation implements MethodInvocation {
      26. private Object targrt;
      27. private Method method;
      28. private Object[] args;
      29. private List methodInterceptorList;
      30. private int count = 1; //调用次数
      31. public MyInvocation(Object targrt, Method method, Object[] args, List methodInterceptorList) {
      32. this.targrt = targrt;
      33. this.method = method;
      34. this.args = args;
      35. this.methodInterceptorList = methodInterceptorList;
      36. }
      37. @Override
      38. public Method getMethod() {
      39. return method;
      40. }
      41. @Override
      42. public Object[] getArguments() {
      43. return args;
      44. }
      45. @Override
      46. public Object proceed() throws Throwable { //调用每一个环绕通知,调用目标
      47. if (count > methodInterceptorList.size()) {
      48. //调用目标,返回并结束通知
      49. return method.invoke(targrt, args);
      50. }
      51. //逐一调用通知
      52. MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);
      53. return methodInterceptor.invoke(this);
      54. }
      55. @Override
      56. public Object getThis() {
      57. return targrt;
      58. }
      59. @Override
      60. public AccessibleObject getStaticPart() {
      61. return method;
      62. }
      63. }
      64. public static void main(String[] args) throws Throwable {
      65. Target target = new Target();
      66. ArrayList list = new ArrayList<>();
      67. list.add(new Advice1());
      68. list.add(new Advice2());
      69. MyInvocation myInvocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
      70. myInvocation.proceed();
      71. }
      72. }

      结果:

      1. Advice1.before()
      2. Advice2.before()
      3. Target.foo()
      4. Advice2.after()
      5. Advice1.after()

       具体过程:

      1. proceed() 方法调用链中下一个环绕通知

      2. 每个环绕通知内部继续调用 proceed()

      3. 调用到没有更多通知了, 就调用目标方法

      MethodInvocation 的编程技巧在实现拦截器、过滤器时能用上

      六、动态通知调用

      带参数绑定的通知方法调用

      1. public class A19 {
      2. @Aspect
      3. static class MyAspect {
      4. @Before("execution(* foo(..))") //静态通知调用,不需要参数绑定,执行时不需要切点
      5. public void before1(){
      6. System.out.println("before1");
      7. }
      8. @Before("execution(* foo(..)) && args(x)") //动态通知调用,需要参数绑定,执行时需要切点对象
      9. public void before2(int x){
      10. System.out.printf("before2(%d)%n", x);
      11. }
      12. }
      13. static class Target {
      14. public void foo(int x) {
      15. System.out.printf("target foo(%d)%n", x);
      16. }
      17. }
      18. @Configuration
      19. static class MyConfig {
      20. @Bean
      21. AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
      22. return new AnnotationAwareAspectJAutoProxyCreator();
      23. }
      24. @Bean
      25. public MyAspect myAspect() {
      26. return new MyAspect();
      27. }
      28. }
      29. public static void main(String[] args) throws Throwable {
      30. GenericApplicationContext context = new GenericApplicationContext();
      31. context.registerBean(ConfigurationClassPostProcessor.class);
      32. context.registerBean(MyConfig.class);
      33. context.refresh();
      34. AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
      35. Method findEligibleAdvisors = creator.getClass().getSuperclass().getSuperclass().
      36. getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
      37. findEligibleAdvisors.setAccessible(true);
      38. //找到和Target匹配的切面
      39. List list = (List) findEligibleAdvisors.invoke(creator, Target.class, "target");
      40. Target target = new Target();
      41. ProxyFactory factory = new ProxyFactory();
      42. factory.setTarget(target);
      43. factory.addAdvisors(list);
      44. //获取代理
      45. Object proxy = factory.getProxy();
      46. //把除了环绕通知以外的其他类型通知统一转换为环绕通知
      47. List interceptorList =
      48. factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);
      49. for (Object o : interceptorList) {
      50. showDetail(o);
      51. }
      52. //创建并执行调用链
      53. Constructor constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(
      54. Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
      55. constructor.setAccessible(true);
      56. ReflectiveMethodInvocation methodInvocation = constructor.newInstance(
      57. proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList);
      58. methodInvocation.proceed();
      59. }
      60. public static void showDetail(Object o) {
      61. try {
      62. Class clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
      63. if (clazz.isInstance(o)) {
      64. Field methodMatcher = clazz.getDeclaredField("methodMatcher");
      65. methodMatcher.setAccessible(true);
      66. Field interceptor = clazz.getDeclaredField("interceptor");
      67. interceptor.setAccessible(true);
      68. System.out.println("环绕通知和切点:" + o);
      69. System.out.println("\t切点为:" + methodMatcher.get(o));
      70. System.out.println("\t通知为:" + interceptor.get(o));
      71. }else {
      72. System.out.println("普通环绕通知:" + o);
      73. }
      74. } catch (Exception e) {
      75. e.printStackTrace();
      76. }
      77. }
      78. }
      79. 结果:

        1. 普通环绕通知:org.springframework.aop.interceptor.ExposeInvocationInterceptor@82de64a
        2. 普通环绕通知:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@659499f1
        3. 环绕通知和切点:org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher@47e2e487
        4. 切点为:AspectJExpressionPointcut: (int x) execution(* foo(..)) && args(x)
        5. 通知为:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@201a4587
        6. before1
        7. before2(100)
        8. target foo(100)

        InterceptorAndDynamicMethodMatcher:拦截器和动态的方法匹配器,内部包含了真正的环绕通知和切点,它并不是一个环绕通知,查看它的类就能知道。

        1. class InterceptorAndDynamicMethodMatcher {
        2. // 环绕通知
        3. final MethodInterceptor interceptor;
        4. // 切点
        5. final MethodMatcher methodMatcher;
        6. public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
        7. this.interceptor = interceptor;
        8. this.methodMatcher = methodMatcher;
        9. }
        10. }

        总结:

        1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知

        2. 所谓动态通知,体现在上面方法的 DynamicInterceptionAdvice 部分,这些通知调用时因为要为通知方法绑定参数,还需再次利用切点表达式

        3. 动态通知调用复杂程度高,性能较低 

      80. 相关阅读:
        Python爬虫教程12:从b站获取神仙姐姐的视频弹幕内容
        [hackthebox] Meow
        【MFC】socket通信代码解析
        WebRTC + Tensorflow.js 在运动健康类项目中的前端应用
        力扣(leetcode)第485题最大连续1的个数(Python)
        GB/T 28181联网系统通信协议结构和技术实现
        Java Web 33道面试题汇总
        检测到 #include 错误。请更新 includePath。无法打开 源 文件xxx
        Vue_Bug VUE-ELEMENT-ADMIN默认是英文模式
        python简单使用grpc
      81. 原文地址:https://blog.csdn.net/qq_51409098/article/details/127729796