SpringAOP模仿实现

打印 上一主题 下一主题

主题 1750|帖子 1750|积分 5250

1_底层切点、通知、切面


注意点:

  • 底层的切点实现
  • 底层的通知实现
  • 底层的切面实现
  • ProxyFactory 用来创建代理

    • 如果指定了接口,且 proxyTargetClass = false,使用 JdkDynamicAopProxy
    • 如果没有指定接口,大概 proxyTargetClass = true,使用 ObjenesisCglibAopProxy
    • 破例:如果目的是接口范例或已经是 jdk 代理,使用 JdkDynamicAopProxy

  1. public class A15 {
  2.     public static void main(String[] args) {
  3.         /*
  4.             两个切面概念
  5.             aspect =
  6.                 通知1(advice) +  切点1(pointcut)
  7.                 通知2(advice) +  切点2(pointcut)
  8.                 通知3(advice) +  切点3(pointcut)
  9.                 ...
  10.             advisor = 更细粒度的切面,包含一个通知和切点
  11.          */
  12.         // 1. 备好切点
  13.         AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  14.         pointcut.setExpression("execution(* foo())");
  15.         // 2. 备好通知
  16.         MethodInterceptor advice = invocation -> {
  17.             System.out.println("before...");
  18.             Object result = invocation.proceed(); // 调用目标
  19.             System.out.println("after...");
  20.             return result;
  21.         };
  22.         // 3. 备好切面
  23.         DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
  24.         /*
  25.            4. 创建代理
  26.                 a. proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
  27.                 b. proxyTargetClass = false,  目标没有实现接口, 用 cglib 实现
  28.                 c. proxyTargetClass = true, 总是使用 cglib 实现
  29.          */
  30.         Target2 target = new Target2();
  31.         ProxyFactory factory = new ProxyFactory();
  32.         factory.setTarget(target);
  33.         factory.addAdvisor(advisor);
  34.         factory.setInterfaces(target.getClass().getInterfaces());
  35.         factory.setProxyTargetClass(false);
  36.         Target2 proxy = (Target2) factory.getProxy();
  37.         System.out.println(proxy.getClass());
  38.         proxy.foo();
  39.         proxy.bar();
  40.         /*
  41.             学到了什么
  42.                 a. Spring 的代理选择规则
  43.                 b. 底层的切点实现
  44.                 c. 底层的通知实现
  45.                 d. ProxyFactory 是用来创建代理的核心实现, 用 AopProxyFactory 选择具体代理实现
  46.                     - JdkDynamicAopProxy
  47.                     - ObjenesisCglibAopProxy
  48.          */
  49.     }
  50.     interface I1 {
  51.         void foo();
  52.         void bar();
  53.     }
  54.     static class Target1 implements I1 {
  55.         public void foo() {
  56.             System.out.println("target1 foo");
  57.         }
  58.         public void bar() {
  59.             System.out.println("target1 bar");
  60.         }
  61.     }
  62.     static class Target2 {
  63.         public void foo() {
  64.             System.out.println("target2 foo");
  65.         }
  66.         public void bar() {
  67.             System.out.println("target2 bar");
  68.         }
  69.     }
  70. }
复制代码
2_切点匹配


切点匹配:

  • 常见 aspectj 切点用法
  • aspectj 切点的范围性,现实的 @Transactional 切点实现
  1. import org.springframework.aop.aspectj.AspectJExpressionPointcut;
  2. import org.springframework.aop.support.StaticMethodMatcherPointcut;
  3. import org.springframework.core.annotation.MergedAnnotations;
  4. import org.springframework.transaction.annotation.Transactional;
  5. import java.lang.reflect.Method;
  6. public class A16 {
  7.     public static void main(String[] args) throws NoSuchMethodException {
  8. //        AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
  9. //        pt1.setExpression("execution(* bar())");
  10. //        System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
  11. //        System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));
  12. //
  13. //        AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
  14. //        pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
  15. //        System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
  16. //        System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));
  17.         StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
  18.             @Override
  19.             public boolean matches(Method method, Class<?> targetClass) {
  20.                 // 检查方法上是否加了 Transactional 注解
  21.                 MergedAnnotations annotations = MergedAnnotations.from(method);
  22.                 if (annotations.isPresent(Transactional.class)) {
  23.                     return true;
  24.                 }
  25.                 // 查看类上是否加了 Transactional 注解
  26.                 annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
  27.                 if (annotations.isPresent(Transactional.class)) {
  28.                     return true;
  29.                 }
  30.                 return false;
  31.             }
  32.         };
  33.         System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
  34.         System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
  35.         System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
  36.         System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));
  37.         /*
  38.             学到了什么
  39.                 a. 底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法
  40.                 b. 比较关键的是它实现了 MethodMatcher 接口, 用来执行方法的匹配
  41.          */
  42.     }
  43.     static class T1 {
  44.         @Transactional
  45.         public void foo() {
  46.         }
  47.         public void bar() {
  48.         }
  49.     }
  50.     @Transactional
  51.     static class T2 {
  52.         public void foo() {
  53.         }
  54.     }
  55.     @Transactional
  56.     interface I3 {
  57.         void foo();
  58.     }
  59.     static class T3 implements I3 {
  60.         public void foo() {
  61.         }
  62.     }
  63. }
复制代码
3_从 @Aspect 到 Advisor

1_代理创建器


注意点:

  • AnnotationAwareAspectJAutoProxyCreator 的作用

    • 将高级 @Aspect 切面统一为低级 Advisor 切面。
    • 在合适的时机创建代理。

  • findEligibleAdvisors 找到有【资格】的 Advisors

    • 有【资格】的 Advisor 一部门是低级的, 可以由自己编写, 如本例 A17 中的 advisor3。
    • 有【资格】的 Advisor 另一部门是高级的, 由解析 @Aspect 后得到。

  • wrapIfNecessary

    • 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则体现必要创建代理。
    • 它的调用时机通常在原始对象初始化后实行, 但遇到循环依赖会提前至依赖注入之前实行。

  1. package org.springframework.aop.framework.autoproxy;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. import org.aspectj.lang.annotation.After;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.aspectj.lang.annotation.Before;
  7. import org.springframework.aop.Advisor;
  8. import org.springframework.aop.aspectj.AspectJExpressionPointcut;
  9. import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
  10. import org.springframework.aop.support.DefaultPointcutAdvisor;
  11. import org.springframework.beans.factory.support.DefaultListableBeanFactory;
  12. import org.springframework.context.annotation.Bean;
  13. import org.springframework.context.annotation.Configuration;
  14. import org.springframework.context.annotation.ConfigurationClassPostProcessor;
  15. import org.springframework.context.support.GenericApplicationContext;
  16. import org.springframework.transaction.annotation.Transactional;
  17. import java.util.List;
  18. /**
  19. * @author shenyang
  20. * @version 1.0
  21. * @info TestAop
  22. * @since 2024/8/17 20:26
  23. */
  24. public class A17 {
  25.     @Aspect//高级切面
  26.     static class Aspect1{
  27.         @Before("execution(* foo())")
  28.         public void before(){
  29.             System.out.println("aspect1 before.....");
  30.         }
  31.         @After("execution(* foo())")
  32.         public void after(){
  33.             System.out.println("aspect1 after.....");
  34.         }
  35.     }
  36.     @Configuration
  37.     static class Config {
  38.         @Bean//低级切面
  39.         public Advisor advisor3(MethodInterceptor advice3) {
  40.             AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  41.             pointcut.setExpression("execution(* foo())");
  42.             return new DefaultPointcutAdvisor(pointcut, advice3);
  43.         }
  44.         @Bean
  45.         public MethodInterceptor advice3(){
  46.             return new MethodInterceptor() {
  47.                 @Override
  48.                 public Object invoke(MethodInvocation methodInvocation) throws Throwable {
  49.                     System.out.println("advice3 before.....");
  50.                     Object proceed = methodInvocation.proceed();
  51.                     System.out.println("advice3 after.....");
  52.                     return proceed;
  53.                 }
  54.             };
  55.         }
  56.     }
  57.     static class T1 {
  58.         public void foo() {
  59.             System.out.println("target1 foo");
  60.         }
  61.     }
  62.     static class T2 {
  63.         public void bar() {
  64.             System.out.println("target2 bar");
  65.         }
  66.     }
  67.     public static void main(String[] args) {
  68.         GenericApplicationContext context = new GenericApplicationContext();
  69.         context.registerBean("aspect1",Aspect1.class);
  70.         context.registerBean("config",Config.class);
  71.         context.registerBean(ConfigurationClassPostProcessor.class);
  72.         context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
  73.         context.refresh();
  74.         AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
  75.         List<Advisor> advisors = creator.findEligibleAdvisors(T2.class, "target2");
  76.         System.out.println("====================");
  77. //        advisors.forEach(System.out::println);
  78.         T1 o1 = (T1) creator.wrapIfNecessary(new T1(), "target1", "target1");
  79.         T2 o2 = (T2) creator.wrapIfNecessary(new T2(), "target2", "target2");
  80.         System.out.println(o1.getClass()+" "+o2.getClass());
  81.         o1.foo();
  82.         o2.bar();
  83.     }
  84. }
复制代码
2_代理创建时机


注意点:

  • 代理的创建时机

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

  • 依赖注入与初始化不应该被加强, 仍应被施加于原始对象
  1. package org.springframework.aop.framework.autoproxy;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. import org.springframework.aop.Advisor;
  5. import org.springframework.aop.aspectj.AspectJExpressionPointcut;
  6. import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
  7. import org.springframework.aop.support.DefaultPointcutAdvisor;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
  12. import org.springframework.context.annotation.Configuration;
  13. import org.springframework.context.annotation.ConfigurationClassPostProcessor;
  14. import org.springframework.context.support.GenericApplicationContext;
  15. import javax.annotation.PostConstruct;
  16. public class A17_1 {
  17.     public static void main(String[] args) {
  18.         GenericApplicationContext context = new GenericApplicationContext();
  19.         context.registerBean(ConfigurationClassPostProcessor.class);
  20.         context.registerBean(Config.class);
  21.         context.refresh();
  22.         context.close();
  23.         // 创建 -> (*) 依赖注入 -> 初始化 (*)
  24.         /*
  25.             学到了什么
  26.                 a. 代理的创建时机
  27.                     1. 初始化之后 (无循环依赖时)
  28.                     2. 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存
  29.                 b. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象
  30.          */
  31.     }
  32.     @Configuration
  33.     static class Config {
  34.         @Bean // 解析 @Aspect、产生代理
  35.         public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
  36.             return new AnnotationAwareAspectJAutoProxyCreator();
  37.         }
  38.         @Bean // 解析 @Autowired
  39.         public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
  40.             return new AutowiredAnnotationBeanPostProcessor();
  41.         }
  42.         @Bean // 解析 @PostConstruct
  43.         public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
  44.             return new CommonAnnotationBeanPostProcessor();
  45.         }
  46.         @Bean
  47.         public Advisor advisor(MethodInterceptor advice) {
  48.             AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  49.             pointcut.setExpression("execution(* foo())");
  50.             return new DefaultPointcutAdvisor(pointcut, advice);
  51.         }
  52.         @Bean
  53.         public MethodInterceptor advice() {
  54.             return (MethodInvocation invocation) -> {
  55.                 System.out.println("before...");
  56.                 return invocation.proceed();
  57.             };
  58.         }
  59.         @Bean
  60.         public Bean1 bean1() {
  61.             return new Bean1();
  62.         }
  63.         @Bean
  64.         public Bean2 bean2() {
  65.             return new Bean2();
  66.         }
  67.     }
  68.     static class Bean1 {
  69.         public void foo() {
  70.         }
  71.         public Bean1() {
  72.             System.out.println("Bean1()");
  73.         }
  74.         @Autowired public void setBean2(Bean2 bean2) {
  75.             System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
  76.         }
  77.         @PostConstruct public void init() {
  78.             System.out.println("Bean1 init()");
  79.         }
  80.     }
  81.     static class Bean2 {
  82.         public Bean2() {
  83.             System.out.println("Bean2()");
  84.         }
  85.         @Autowired public void setBean1(Bean1 bean1) {
  86.             System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
  87.         }
  88.         @PostConstruct public void init() {
  89.             System.out.println("Bean2 init()");
  90.         }
  91.     }
  92. }
复制代码
3_@Before 对应的低级通知


注意点:

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

    • 通知代码从哪儿来
    • 切点是什么(这里为啥要切点, 后面表明)
    • 通知对象如何创建, 本例共用同一个 Aspect 对象

  • 雷同的还有

    • AspectJAroundAdvice (围绕通知)
    • AspectJAfterReturningAdvice
    • AspectJAfterThrowingAdvice (围绕通知)
    • AspectJAfterAdvice (围绕通知)

  1. package org.springframework.aop.framework.autoproxy;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.Before;
  4. import org.springframework.aop.Advisor;
  5. import org.springframework.aop.aspectj.AspectInstanceFactory;
  6. import org.springframework.aop.aspectj.AspectJExpressionPointcut;
  7. import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
  8. import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
  9. import org.springframework.aop.support.DefaultPointcutAdvisor;
  10. import java.lang.reflect.Method;
  11. import java.util.ArrayList;
  12. import java.util.List;
  13. public class A17_2 {
  14.     static class Aspect {
  15.         @Before("execution(* foo())")
  16.         public void before1() {
  17.             System.out.println("before1");
  18.         }
  19.         @Before("execution(* foo())")
  20.         public void before2() {
  21.             System.out.println("before2");
  22.         }
  23.         public void after() {
  24.             System.out.println("after");
  25.         }
  26.         public void afterReturning() {
  27.             System.out.println("afterReturning");
  28.         }
  29.         public void afterThrowing() {
  30.             System.out.println("afterThrowing");
  31.         }
  32.         public Object around(ProceedingJoinPoint pjp) throws Throwable {
  33.             try {
  34.                 System.out.println("around...before");
  35.                 return pjp.proceed();
  36.             } finally {
  37.                 System.out.println("around...after");
  38.             }
  39.         }
  40.     }
  41.     static class Target {
  42.         public void foo() {
  43.             System.out.println("target foo");
  44.         }
  45.     }
  46.     @SuppressWarnings("all")
  47.     public static void main(String[] args) throws Throwable {
  48.         AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
  49.         // 高级切面转低级切面类
  50.         List<Advisor> list = new ArrayList<>();
  51.         for (Method method : Aspect.class.getDeclaredMethods()) {
  52.             if (method.isAnnotationPresent(Before.class)) {
  53.                 // 解析切点
  54.                 String expression = method.getAnnotation(Before.class).value();
  55.                 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  56.                 pointcut.setExpression(expression);
  57.                 // 通知类
  58.                 AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
  59.                 // 切面
  60.                 Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
  61.                 list.add(advisor);
  62.             }
  63.         }
  64.         for (Advisor advisor : list) {
  65.             System.out.println(advisor);
  66.         }
  67.         /*
  68.             @Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
  69.                 a. 通知代码从哪儿来
  70.                 b. 切点是什么(这里为啥要切点, 后面解释)
  71.                 c. 通知对象如何创建, 本例共用同一个 Aspect 对象
  72.             类似的通知还有
  73.                 1. AspectJAroundAdvice (环绕通知)
  74.                 2. AspectJAfterReturningAdvice
  75.                 3. AspectJAfterThrowingAdvice
  76.                 4. AspectJAfterAdvice (环绕通知)
  77.          */
  78.     }
  79. }
复制代码
4_静态通知调用


代理对象调用流程如下(以 JDK 动态代理实现为例)


  • 从 ProxyFactory 得到 Target 和围绕通知链,根据他俩创建 MethodInvocation,简称 mi
  • 初次实行 mi.proceed() 发现有下一个围绕通知,调用它的 invoke(mi)
  • 进入围绕通知1,实行前加强,再次调用 mi.proceed() 发现有下一个围绕通知,调用它的 invoke(mi)
  • 进入围绕通知2,实行前加强,调用 mi.proceed() 发现没有围绕通知,调用 mi.invokeJoinPoint() 实行目的方法
  • 目的方法实行结束,将结果返回给围绕通知2,实行围绕通知2 的后加强
  • 围绕通知2继续将结果返回给围绕通知1,实行围绕通知1 的后加强
  • 围绕通知1返回最终的结果
图中不同颜色对应一次围绕通知或目的的调用起始至终结
     1_通知调用过程


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

  • 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 围绕通知

    • MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
    • AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
    • 这体现的是适配器设计模式

  • 所谓静态通知,体现在上面方法的 Interceptors 部门,这些通知调用时无需再次查抄切点,直接调用即可
  • 结合目的与围绕通知链,创建 MethodInvocation 对象,通过它完成整个调用
  1. package org.springframework.aop.framework;
  2. import org.aopalliance.intercept.MethodInvocation;
  3. import org.aspectj.lang.ProceedingJoinPoint;
  4. import org.aspectj.lang.annotation.AfterReturning;
  5. import org.aspectj.lang.annotation.AfterThrowing;
  6. import org.aspectj.lang.annotation.Around;
  7. import org.aspectj.lang.annotation.Before;
  8. import org.springframework.aop.Advisor;
  9. import org.springframework.aop.aspectj.*;
  10. import org.springframework.aop.framework.ProxyFactory;
  11. import org.springframework.aop.framework.ReflectiveMethodInvocation;
  12. import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
  13. import org.springframework.aop.support.DefaultPointcutAdvisor;
  14. import java.lang.reflect.Method;
  15. import java.util.ArrayList;
  16. import java.util.List;
  17. public class A18 {
  18.     static class Aspect {
  19.         @Before("execution(* foo())")
  20.         public void before1() {
  21.             System.out.println("before1");
  22.         }
  23.         @Before("execution(* foo())")
  24.         public void before2() {
  25.             System.out.println("before2");
  26.         }
  27.         public void after() {
  28.             System.out.println("after");
  29.         }
  30.         @AfterReturning("execution(* foo())")
  31.         public void afterReturning() {
  32.             System.out.println("afterReturning");
  33.         }
  34.         @AfterThrowing("execution(* foo())")
  35.         public void afterThrowing(Exception e) {
  36.             System.out.println("afterThrowing " + e.getMessage());
  37.         }
  38.         @Around("execution(* foo())")
  39.         public Object around(ProceedingJoinPoint pjp) throws Throwable {
  40.             try {
  41.                 System.out.println("around...before");
  42.                 return pjp.proceed();
  43.             } finally {
  44.                 System.out.println("around...after");
  45.             }
  46.         }
  47.     }
  48.     static class Target {
  49.         public void foo() {
  50.             System.out.println("target foo");
  51.         }
  52.     }
  53.     @SuppressWarnings("all")
  54.     public static void main(String[] args) throws Throwable {
  55.         AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
  56.         // 1. 高级切面转低级切面类
  57.         List<Advisor> list = new ArrayList<>();
  58.         for (Method method : Aspect.class.getDeclaredMethods()) {
  59.             if (method.isAnnotationPresent(Before.class)) {
  60.                 // 解析切点
  61.                 String expression = method.getAnnotation(Before.class).value();
  62.                 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  63.                 pointcut.setExpression(expression);
  64.                 // 通知类
  65.                 AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
  66.                 // 切面
  67.                 Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
  68.                 list.add(advisor);
  69.             } else if (method.isAnnotationPresent(AfterReturning.class)) {
  70.                 // 解析切点
  71.                 String expression = method.getAnnotation(AfterReturning.class).value();
  72.                 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  73.                 pointcut.setExpression(expression);
  74.                 // 通知类
  75.                 AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
  76.                 // 切面
  77.                 Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
  78.                 list.add(advisor);
  79.             } else if (method.isAnnotationPresent(Around.class)) {
  80.                 // 解析切点
  81.                 String expression = method.getAnnotation(Around.class).value();
  82.                 AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  83.                 pointcut.setExpression(expression);
  84.                 // 通知类
  85.                 AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
  86.                 // 切面
  87.                 Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
  88.                 list.add(advisor);
  89.             }
  90.         }
  91.         for (Advisor advisor : list) {
  92.             System.out.println(advisor);
  93.         }
  94.         /*
  95.             @Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
  96.                 a. 通知代码从哪儿来
  97.                 b. 切点是什么
  98.                 c. 通知对象如何创建, 本例共用同一个 Aspect 对象
  99.             类似的通知还有
  100.                 1. AspectJAroundAdvice (环绕通知)
  101.                 2. AspectJAfterReturningAdvice
  102.                 3. AspectJAfterThrowingAdvice (环绕通知)
  103.                 4. AspectJAfterAdvice (环绕通知)
  104.          */
  105.         // 2. 通知统一转换为环绕通知 MethodInterceptor
  106.         /*
  107.             其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象
  108.                 a. 因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象, 即 MethodInvocation
  109.                 b. MethodInvocation 要知道 advice 有哪些, 还要知道目标, 调用次序如下
  110.                 将 MethodInvocation 放入当前线程
  111.                     |-> before1 ----------------------------------- 从当前线程获取 MethodInvocation
  112.                     |                                             |
  113.                     |   |-> before2 --------------------          | 从当前线程获取 MethodInvocation
  114.                     |   |                              |          |
  115.                     |   |   |-> target ------ 目标   advice2    advice1
  116.                     |   |                              |          |
  117.                     |   |-> after2 ---------------------          |
  118.                     |                                             |
  119.                     |-> after1 ------------------------------------
  120.                 c. 从上图看出, 环绕通知才适合作为 advice, 因此其他 before、afterReturning 都会被转换成环绕通知
  121.                 d. 统一转换为环绕通知, 体现的是设计模式中的适配器模式
  122.                     - 对外是为了方便使用要区分 before、afterReturning
  123.                     - 对内统一都是环绕通知, 统一用 MethodInterceptor 表示
  124.             此步获取所有执行时需要的 advice (静态)
  125.                 a. 即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上
  126.                 b. 适配如下
  127.                   - MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
  128.                   - AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
  129.          */
  130.         Target target = new Target();
  131.         ProxyFactory proxyFactory = new ProxyFactory();
  132.         proxyFactory.setTarget(target);
  133.         proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程
  134.         proxyFactory.addAdvisors(list);
  135.         System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
  136.         List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
  137.         for (Object o : methodInterceptorList) {
  138.             System.out.println(o);
  139.         }
  140.         System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
  141.         // 3. 创建并执行调用链 (环绕通知s + 目标)
  142.         MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
  143.                 null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
  144.         );
  145.         methodInvocation.proceed();
  146.         /*
  147.             学到了什么
  148.                 a. 无参数绑定的通知如何被调用
  149.                 b. MethodInvocation 编程技巧: 拦截器、过滤器等等实现都与此类似
  150.                 c. 适配器模式在 Spring 中的体现
  151.          */
  152.     }
  153. }
复制代码
2_模仿 MethodInvocation


注意点:

  • proceed() 方法调用链中下一个围绕通知
  • 每个围绕通知内部继续调用 proceed()
  • 调用到没有更多通知了, 就调用目的方法
MethodInvocation 的编程技巧在实现拦截器、过滤器时能用上
  1. package org.springframework.aop.framework;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. import java.lang.reflect.AccessibleObject;
  5. import java.lang.reflect.Method;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. /*
  9.     模拟调用链过程, 是一个简单的递归过程
  10.         1. proceed() 方法调用链中下一个环绕通知
  11.         2. 每个环绕通知内部继续调用 proceed()
  12.         3. 调用到没有更多通知了, 就调用目标方法
  13. */
  14. public class A18_1 {
  15.     static class Target {
  16.         public void foo() {
  17.             System.out.println("Target.foo()");
  18.         }
  19.     }
  20.     static class Advice1 implements MethodInterceptor {
  21.         public Object invoke(MethodInvocation invocation) throws Throwable {
  22.             System.out.println("Advice1.before()");
  23.             Object result = invocation.proceed();// 调用下一个通知或目标
  24.             System.out.println("Advice1.after()");
  25.             return result;
  26.         }
  27.     }
  28.     static class Advice2 implements MethodInterceptor {
  29.         public Object invoke(MethodInvocation invocation) throws Throwable {
  30.             System.out.println("Advice2.before()");
  31.             Object result = invocation.proceed();// 调用下一个通知或目标
  32.             System.out.println("Advice2.after()");
  33.             return result;
  34.         }
  35.     }
  36.     static class MyInvocation implements MethodInvocation {
  37.         private Object target;  // 1
  38.         private Method method;
  39.         private Object[] args;
  40.         List<MethodInterceptor> methodInterceptorList; // 2
  41.         private int count = 1; // 调用次数
  42.         public MyInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {
  43.             this.target = target;
  44.             this.method = method;
  45.             this.args = args;
  46.             this.methodInterceptorList = methodInterceptorList;
  47.         }
  48.         @Override
  49.         public Method getMethod() {
  50.             return method;
  51.         }
  52.         @Override
  53.         public Object[] getArguments() {
  54.             return args;
  55.         }
  56.         @Override
  57.         public Object proceed() throws Throwable { // 调用每一个环绕通知, 调用目标
  58.             if (count > methodInterceptorList.size()) {
  59.                 // 调用目标, 返回并结束递归
  60.                 return method.invoke(target, args);
  61.             }
  62.             // 逐一调用通知, count + 1
  63.             MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);
  64.             return methodInterceptor.invoke(this);
  65.         }
  66.         @Override
  67.         public Object getThis() {
  68.             return target;
  69.         }
  70.         @Override
  71.         public AccessibleObject getStaticPart() {
  72.             return method;
  73.         }
  74.     }
  75.     public static void main(String[] args) throws Throwable {
  76.         Target target = new Target();
  77.         List<MethodInterceptor> list = new ArrayList<>();
  78.         list.add(new Advice1());
  79.         list.add(new Advice2());
  80.         MyInvocation invocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
  81.         invocation.proceed();
  82.     }
  83. }
复制代码
5_动态通知调用


注意点:

  • 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 围绕通知
  • 所谓动态通知,体现在上面方法的 DynamicInterceptionAdvice 部门,这些通知调用时因为要为通知方法绑定参数,还需再次利用切点表达式
  • 动态通知调用复杂水平高,性能较低
  1. package org.springframework.aop.framework.autoproxy;
  2. import org.aspectj.lang.annotation.Aspect;
  3. import org.aspectj.lang.annotation.Before;
  4. import org.springframework.aop.Advisor;
  5. import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
  6. import org.springframework.aop.framework.ProxyFactory;
  7. import org.springframework.aop.framework.ReflectiveMethodInvocation;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. import org.springframework.context.annotation.ConfigurationClassPostProcessor;
  11. import org.springframework.context.support.GenericApplicationContext;
  12. import java.lang.reflect.Field;
  13. import java.util.List;
  14. public class A19 {
  15.     @Aspect
  16.     static class MyAspect {
  17.         @Before("execution(* foo(..))") // 静态通知调用,不带参数绑定,执行时不需要切点
  18.         public void before1() {
  19.             System.out.println("before1");
  20.         }
  21.         @Before("execution(* foo(..)) && args(x)") // 动态通知调用,需要参数绑定,执行时还需要切点对象
  22.         public void before2(int x) {
  23.             System.out.printf("before2(%d)%n", x);
  24.         }
  25.     }
  26.     static class Target {
  27.         public void foo(int x) {
  28.             System.out.printf("target foo(%d)%n", x);
  29.         }
  30.     }
  31.     @Configuration
  32.     static class MyConfig {
  33.         @Bean
  34.         AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
  35.             return new AnnotationAwareAspectJAutoProxyCreator();
  36.         }
  37.         @Bean
  38.         public MyAspect myAspect() {
  39.             return new MyAspect();
  40.         }
  41.     }
  42.     public static void main(String[] args) throws Throwable {
  43.         GenericApplicationContext context = new GenericApplicationContext();
  44.         context.registerBean(ConfigurationClassPostProcessor.class);
  45.         context.registerBean(MyConfig.class);
  46.         context.refresh();
  47.         AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
  48.         List<Advisor> list = creator.findEligibleAdvisors(Target.class, "target");
  49.         Target target = new Target();
  50.         ProxyFactory factory = new ProxyFactory();
  51.         factory.setTarget(target);
  52.         factory.addAdvisors(list);
  53.         Object proxy = factory.getProxy(); // 获取代理
  54.         List<Object> interceptorList = factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);
  55.         for (Object o : interceptorList) {
  56.             showDetail(o);
  57.         }
  58.         System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
  59.         ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(
  60.                 proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList
  61.         ) {};
  62.         invocation.proceed();
  63.         /*
  64.             学到了什么
  65.                 a. 有参数绑定的通知调用时还需要切点,对参数进行匹配及绑定
  66.                 b. 复杂程度高, 性能比无参数绑定的通知调用低
  67.          */
  68.     }
  69.     public static void showDetail(Object o) {
  70.         try {
  71.             Class<?> clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
  72.             if (clazz.isInstance(o)) {
  73.                 Field methodMatcher = clazz.getDeclaredField("methodMatcher");
  74.                 methodMatcher.setAccessible(true);
  75.                 Field methodInterceptor = clazz.getDeclaredField("interceptor");
  76.                 methodInterceptor.setAccessible(true);
  77.                 System.out.println("环绕通知和切点:" + o);
  78.                 System.out.println("\t切点为:" + methodMatcher.get(o));
  79.                 System.out.println("\t通知为:" + methodInterceptor.get(o));
  80.             } else {
  81.                 System.out.println("普通环绕通知:" + o);
  82.             }
  83.         } catch (Exception e) {
  84.             e.printStackTrace();
  85.         }
  86.     }
  87. }
复制代码
补充—— Advice 常见子接口:
加强范例常见 Advice 子接口/子类描述前置通知MethodBeforeAdvice方法调用前实行后置通知AfterReturningAdvice方法调用后且正常返回时实行围绕通知MethodInterceptor方法调用前后都可加强,控制流程异常通知ThrowsAdvice方法抛出异常时加强引介通知DelegatingIntroductionInterceptor为目的对象动态添加接口实现


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

兜兜零元

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表