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 类图

Spring原理学习(八)AOP底层实现_第1张图片

1. Advisor 可以获取到 Advice。

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

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

Spring原理学习(八)AOP底层实现_第2张图片

2、Advice

Spring原理学习(八)AOP底层实现_第3张图片

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

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

3、Advisor

Spring原理学习(八)AOP底层实现_第4张图片

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

PointcutAdvisor

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

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

4、Advised

Spring原理学习(八)AOP底层实现_第5张图片

Spring原理学习(八)AOP底层实现_第6张图片 

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

Spring原理学习(八)AOP底层实现_第7张图片

先说结论: 

  • proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
  • proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
  • proxyTargetClass = true, 总是使用 cglib 实现
public class A15 {
    public static void main(String[] args) {

        // 1. 备好切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");

        // 2. 备好通知 ,注意同名MethodInterceptor
        MethodInterceptor advice = new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("before...");
                //调用目标
                Object result = invocation.proceed();
                System.out.println("after...");
                return result;
            }
        };

        // 3. 备好切面
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
          
        //4. 创建代理   
        Target1 target1 = new Target1();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target1);
        factory.addAdvisor(advisor);

        I1 proxy = (I1) factory.getProxy();
        
        //5. 调用增强后的方法
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();
    }

    interface I1 {
        void foo();

        void bar();
    }

    static class Target1 implements I1 {
        public void foo() {
            System.out.println("target1 foo");
        }

        public void bar() {
            System.out.println("target1 bar");
        }
    }

    static class Target2 {
        public void foo() {
            System.out.println("target2 foo");
        }

        public void bar() {
            System.out.println("target2 bar");
        }
    }
}

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

class com.itheima.a15.A15$Target1$$EnhancerBySpringCGLIB$$88a7fb77
before...
target1 foo
after...
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、根据指定的方法进行匹配

public class A16 {
    public static void main(String[] args) throws NoSuchMethodException {
        AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
        //根据指定的方法进行匹配
        pt1.setExpression("execution(* bar())");
        //判断是否匹配
        System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));
    }

    static class T1 {
        public void foo() {
        }
        public void bar() {
        }
    }
}

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

结果:

false
true

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

public class A16 {
    public static void main(String[] args) throws NoSuchMethodException {
        AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
        //根据方法上的注解进行匹配
        pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
        System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));
    }

    static class T1 {
        @Transactional
        public void foo() {
        }
        public void bar() {
        }
    }
}

结果:

true
false

3、自己实现切点匹配

MergedAnnotations是一个注解的工具类

public class A16 {
    public static void main(String[] args) throws NoSuchMethodException {
        StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class targetClass) {
                /* 检查方法上是否加了 Transactional 注解 */

                //获取方法上标注的注解
                MergedAnnotations annotations = MergedAnnotations.from(method);
                //是否有@Transactional注解
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }

                /* 查看类上是否加了 Transactional 注解 */

                //获取类上标注的注解
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                //是否有@Transactional注解
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                return false;
            }
        };

        System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
        System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
        System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
        System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));
    }


    static class T1 {
        @Transactional
        public void foo() {
        }
        public void bar() {
        }
    }

    @Transactional
    static class T2 {
        public void foo() {
        }
    }

    @Transactional
    interface I3 {
        void foo();
    }
    static class T3 implements I3 {
        public void foo() {
        }
    }
}

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

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

所以更换了策略 

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

默认策略的结果:

true
false
true
false

指定为 TYPE_HIERARCHY 策略的结果:

true
false
true
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 切面

  • 在合适的时机创建代理

public class A17 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("aspect1", Aspect1.class);
        context.registerBean("config", Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);

        //解析@Aspect,产生代理
        context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

        context.refresh();

        /*
        第一个重要方法 findEligibleAdvisors:找到有资格的Advisors
            a. 有资格的 Advisor 一部分是低级的,可以由自己编写,如下例中的 advisor3
            b. 有资格的 Advisor 一部分是高级的,由解析 @Aspect 后获得
         */
        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Method findEligibleAdvisors = creator.getClass().getSuperclass().getSuperclass().
                getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
        findEligibleAdvisors.setAccessible(true);
        //找到和 Target1 类匹配的所有切面
        // 参数一:目标类型,参数二:bean名字,由于没交给spring管理,随便写一个
        List advisors = (List) findEligibleAdvisors.invoke(creator, Target1.class, "target1");
        for (Advisor advisor : advisors) {
            System.out.println(advisor);
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");


        /*
        第二个重要方法 wrapIfNecessary
            a. 它内部调用 findEligibleAdvisors,只要返回集合不为空,则表示需要创建代理
         */

        //参数一:目标对象 参数二:bean名字,由于没交给spring管理,随便写一个 参数三:对于这个操作没用,随便写一个
        Method wrapIfNecessary = creator.getClass().getSuperclass().getSuperclass().getSuperclass().
                getDeclaredMethod("wrapIfNecessary", Object.class, String.class, Object.class);
        wrapIfNecessary.setAccessible(true);
        Object o1 = wrapIfNecessary.invoke(creator, new Target1(), "target1", "target1");
        //代理对象
        System.out.println(o1.getClass());
        Object o2 = wrapIfNecessary.invoke(creator, new Target2(), "target2", "target2");
        //对象本身
        System.out.println(o2.getClass());

        ((Target1) o1).foo();

    }
    static class Target1 {
        public void foo(){
            System.out.println("target1 foo");
        }
    }

    static class Target2 {
        public void bar(){
            System.out.println("target2 bar");
        }
    }

    @Aspect //高级切面类
    static class Aspect1 {
        @Before("execution(* foo())")
        public void before(){
            System.out.println("aspect1 before...");
        }

        @After("execution(* foo())")
        public void after(){
            System.out.println("aspect1 after...");
        }
    }

    @Configuration
    static class Config {
        @Bean//低级切面
        public Advisor advisor3(MethodInterceptor advice3){
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
            return advisor;
        }

        @Bean
        public MethodInterceptor advice3() {
            return new MethodInterceptor() {
                @Override
                public Object invoke(MethodInvocation invocation) throws Throwable {
                    System.out.println("advice3 before...");
                    Object result = invocation.proceed();
                    System.out.println("advice3 after...");
                    return result;
                }
            };
        }
    }
}

结果:

//spring加的,暴露调用器的拦截器,作用后面会讲
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR

//低级切面
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [com.itheima.a17.A17$Config$1@209da20d]

//高级切面转换后的低级切面
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.itheima.a17.A17$Aspect1.before()]; perClauseKind=SINGLETON
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void com.itheima.a17.A17$Aspect1.after()]; perClauseKind=SINGLETON

>>>>>>>>>>>>>>>>>>>>>>>>>>
[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 
class com.itheima.a17.A17$Target1$$EnhancerBySpringCGLIB$$a2f64ee5
class com.itheima.a17.A17$Target2

advice3 before...
aspect1 before...
target1 foo
aspect1 after...
advice3 after...

总结

  1. AnnotationAwareAspectJAutoProxyCreator 的作用

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

    • 在合适的时机创建代理

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

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

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

  3. wrapIfNecessary

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

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

2、代理创建时机

1)代理的创建时机:

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

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

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

无循环依赖

public class A17_1 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(Config.class);
        context.registerBean(ConfigurationClassPostProcessor.class);

        context.refresh();
    }

    @Configuration
    static class Config {
        @Bean //解析 @Aspect,产生代理
        public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
            return new AnnotationAwareAspectJAutoProxyCreator();
        }

        @Bean //解析 @Autowired
        public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
            return new AutowiredAnnotationBeanPostProcessor();
        }

        @Bean //解析 @PostConstruct
        public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
            return new CommonAnnotationBeanPostProcessor();
        }

        @Bean
        public Advisor advisor(MethodInterceptor advice) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            return new DefaultPointcutAdvisor(pointcut, advice);
        }

        @Bean
        public MethodInterceptor advice(){
            return new MethodInterceptor() {
                @Override
                public Object invoke(MethodInvocation invocation) throws Throwable {
                    System.out.println("before...");
                    Object result = invocation.proceed();
                    return result;
                }
            };
        }

        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {

        public void foo(){

        }

        public Bean1(){
            System.out.println("Bean1()");
        }

        @PostConstruct
        public void init() {
            System.out.println("Bean1 init()");
        }
    }

    static class Bean2 {

        public Bean2(){
            System.out.println("Bean2()");
        }

        @Autowired
        public void setBean1(Bean1 bean1) {
            System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
        }

        @PostConstruct
        public void init() {
            System.out.println("Bean2 init()");
        }
    }
}

结果:

Bean1()
Bean1 init()
[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 
Bean2()
Bean2 setBean1(bean1) class is: class com.itheima.a17.A17_1$Bean1$$EnhancerBySpringCGLIB$$b0011e5d
Bean2 init()

有循环依赖时

    static class Bean1 {

        public void foo(){

        }

        public Bean1(){
            System.out.println("Bean1()");
        }

        @Autowired
        public void setBean1(Bean2 bean2) {
            System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
        }

        @PostConstruct
        public void init() {
            System.out.println("Bean1 init()");
        }
    }

    static class Bean2 {

        public Bean2(){
            System.out.println("Bean2()");
        }

        @Autowired
        public void setBean1(Bean1 bean1) {
            System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
        }

        @PostConstruct
        public void init() {
            System.out.println("Bean2 init()");
        }
    }

结果:

Bean1()
Bean2()
[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 
Bean2 setBean1(bean1) class is: class com.itheima.a17.A17_1$Bean1$$EnhancerBySpringCGLIB$$2d7cace9
Bean2 init()
Bean1 setBean2(bean2) class is: class com.itheima.a17.A17_1$Bean2
Bean1 init()

3、@Before 对应的低级通知

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

  1. 通知代码从哪儿来

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

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

类似的还有:

  1. AspectJAroundAdvice (环绕通知)

  2. AspectJAfterReturningAdvice

  3. AspectJAfterThrowingAdvice (环绕通知)

  4. AspectJAfterAdvice (环绕通知)

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

public class A17_2 {
    static class Aspect {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("before2");
        }

        public void after() {
            System.out.println("after");
        }
    }

    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }

    public static void main(String[] args) {
        //切面对象工厂,将来由工厂拿到切面对象
        AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
        List list = new ArrayList<>();
        //高级切面转低级切面类
        for (Method method : Aspect.class.getDeclaredMethods()) {
            //方法上是否标注 @Before 注解
            if (method.isAnnotationPresent(Before.class)) {
                //解析切点
                String expression = method.getAnnotation(Before.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                //通知类
                //参数三:将来反射调用切面类的方法需要切面类对象,所以需要传入切面类对象,具体从工厂获取
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);

                //切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

                list.add(advisor);
            }
        }
        for (Advisor advisor : list) {
            System.out.println(advisor);
        }
    }
}

结果:

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

org.springframework.aop.support.DefaultPointcutAdvisor: 
pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: 
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 有哪些,还要知道目标,调用次序

Spring原理学习(八)AOP底层实现_第8张图片

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

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

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

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

适配如下

  • MethodBeforeAdviceAdapter 将 AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
  • AfterReturningAdviceAdapter 将 AspectJAfterReturningAdvice 适配为 MethodAfterReturningAdviceInterceptor
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    MethodBeforeAdviceAdapter() {
    }

    //支持哪种通知的适配
    public boolean supportsAdvice(Advice advice) {
        return advice instanceof MethodBeforeAdvice;
    }

    //转换成 MethodInterceptor 接口
    public MethodInterceptor getInterceptor(Advisor advisor) {
        MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }
}

2、模拟静态通知调用

public class A18 {
    static class Aspect {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("before2");
        }

        public void after() {
            System.out.println("after");
        }

        @AfterReturning("execution(* foo())")
        public void afterReturning() {
            System.out.println("afterReturning");
        }

        @AfterThrowing("execution(* foo())")
        public void afterThrowing(Exception e) {
            System.out.println("afterThrowing" + e.getMessage());
        }

        @Around("execution(* foo())")
        public Object around(ProceedingJoinPoint pjp) throws Throwable{
            System.out.println("around before...");
            Object result = pjp.proceed();
            System.out.println("around after...");
            return result;
        }
    }

    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }

    public static void main(String[] args) throws Throwable {

        /* 1. 高级切面转低级切面类 */

        //切面对象工厂,由工厂拿到切面对象
        AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
        List list = new ArrayList<>();
        for (Method method : Aspect.class.getDeclaredMethods()) {
            //方法上是否标注 @Before 注解
            if (method.isAnnotationPresent(Before.class)) {
                //解析切点
                String expression = method.getAnnotation(Before.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);

                //通知类
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);

                //切面
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

                list.add(advisor);
            } else if (method.isAnnotationPresent(AfterReturning.class)) {
                //方法上是否标注 @AfterReturning 注解
                if (method.isAnnotationPresent(AfterReturning.class)) {
                    //解析切点
                    String expression = method.getAnnotation(AfterReturning.class).value();
                    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                    pointcut.setExpression(expression);

                    //通知类
                    AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);

                    //切面
                    DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

                    list.add(advisor);
                }
            } else if (method.isAnnotationPresent(Around.class)) {
                //方法上是否标注 @Around 注解
                if (method.isAnnotationPresent(Around.class)) {
                    //解析切点
                    String expression = method.getAnnotation(Around.class).value();
                    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                    pointcut.setExpression(expression);

                    //通知类
                    AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);

                    //切面
                    DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

                    list.add(advisor);
                }
            }
        }

        for (Advisor advisor : list) {
            System.out.println(advisor);
        }

        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>");

        /* 省略了 advisor 排序步骤 */

        /* 2. 通知统一转换为环绕通知 MethodInterceptor */

        ProxyFactory proxyFactory = new ProxyFactory();
        Target target = new Target();
        proxyFactory.setTarget(target);
        //准备把 MethodInvocation 放入当前通知
        proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); 
        proxyFactory.addAdvisors(list);

        //把除了环绕通知以外的其他类型通知统一转换为环绕通知
        List methodInterceptorList =
                proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
        for (Object o : methodInterceptorList) {
            System.out.println(o);
        }

        /* 3. 创建并执行调用链 (所有的环绕通知 + 目标) */
        Constructor constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(
                Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
        constructor.setAccessible(true);
        ReflectiveMethodInvocation methodInvocation = constructor.newInstance(
                null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList);
        methodInvocation.proceed();
    /*
        此步模拟调用链过程,是一个简单的递归调用
            1. proceed() 方法调用链中下一个环绕通知
            2. 每个环绕通知内部继续调用 proceed()
            3. 调用到没有更多通知了,就调用目标方法
     */
    }
}
 
  

结果:

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 '']
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 '']
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 '']
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 '']
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
org.springframework.aop.interceptor.ExposeInvocationInterceptor@8e24743
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 ''
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@74a10858
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@23fe1d71
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@28ac3dc3
around before...
before1
before2
target foo
afterReturning
around after...

ExposeInvocationInterceptor的作用

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

Spring原理学习(八)AOP底层实现_第9张图片

如何放置?

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

总结 

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

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

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

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

3、模拟 MethodInvocation

用到的是职责链模式

public class A18_1 {

    static class Target {
        public void foo() {
            System.out.println("Target.foo()");
        }
    }

    static class Advice1 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("Advice1.before()");
            Object result = invocation.proceed(); //调用下一个通知目标
            System.out.println("Advice1.after()");
            return result;
        }
    }

    static class Advice2 implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("Advice2.before()");
            Object result = invocation.proceed(); //调用下一个通知目标
            System.out.println("Advice2.after()");
            return result;
        }
    }

    static class MyInvocation implements MethodInvocation {

        private Object targrt;
        private Method method;
        private Object[] args;
        private List methodInterceptorList;
        private int count = 1; //调用次数

        public MyInvocation(Object targrt, Method method, Object[] args, List methodInterceptorList) {
            this.targrt = targrt;
            this.method = method;
            this.args = args;
            this.methodInterceptorList = methodInterceptorList;
        }

        @Override
        public Method getMethod() {
            return method;
        }

        @Override
        public Object[] getArguments() {
            return args;
        }

        @Override
        public Object proceed() throws Throwable { //调用每一个环绕通知,调用目标
            if (count > methodInterceptorList.size()) {
                //调用目标,返回并结束通知
                return method.invoke(targrt, args);
            }
            //逐一调用通知
            MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);
            return methodInterceptor.invoke(this);
        }

        @Override
        public Object getThis() {
            return targrt;
        }

        @Override
        public AccessibleObject getStaticPart() {
            return method;
        }
    }

    public static void main(String[] args) throws Throwable {
        Target target = new Target();
        ArrayList list = new ArrayList<>();
        list.add(new Advice1());
        list.add(new Advice2());
        MyInvocation myInvocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
        myInvocation.proceed();
    }
}

结果:

Advice1.before()
Advice2.before()
Target.foo()
Advice2.after()
Advice1.after()

 具体过程:

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

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

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

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

六、动态通知调用

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

public class A19 {
    @Aspect
    static class MyAspect {
        @Before("execution(* foo(..))") //静态通知调用,不需要参数绑定,执行时不需要切点
        public void before1(){
            System.out.println("before1");
        }

        @Before("execution(* foo(..)) && args(x)") //动态通知调用,需要参数绑定,执行时需要切点对象
        public void before2(int x){
            System.out.printf("before2(%d)%n", x);
        }
    }

    static class Target {
        public void foo(int x) {
            System.out.printf("target foo(%d)%n", x);
        }
    }

    @Configuration
    static class MyConfig {
        @Bean
        AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
            return new AnnotationAwareAspectJAutoProxyCreator();
        }

        @Bean
        public MyAspect myAspect() {
            return new MyAspect();
        }
    }

    public static void main(String[] args) throws Throwable {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(MyConfig.class);
        context.refresh();
        
        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        Method findEligibleAdvisors = creator.getClass().getSuperclass().getSuperclass().
                getDeclaredMethod("findEligibleAdvisors", Class.class, String.class);
        findEligibleAdvisors.setAccessible(true);
        //找到和Target匹配的切面
        List list = (List) findEligibleAdvisors.invoke(creator, Target.class, "target");

        Target target = new Target();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvisors(list);
        //获取代理
        Object proxy = factory.getProxy();

        //把除了环绕通知以外的其他类型通知统一转换为环绕通知
        List interceptorList =
                factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);

        for (Object o : interceptorList) {
            showDetail(o);
        }

        //创建并执行调用链
        Constructor constructor = ReflectiveMethodInvocation.class.getDeclaredConstructor(
                Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
        constructor.setAccessible(true);
        ReflectiveMethodInvocation methodInvocation = constructor.newInstance(
                proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList);
        methodInvocation.proceed();
    }

    public static void showDetail(Object o) {
        try {
            Class clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
            if (clazz.isInstance(o)) {
                Field methodMatcher = clazz.getDeclaredField("methodMatcher");
                methodMatcher.setAccessible(true);
                Field interceptor = clazz.getDeclaredField("interceptor");
                interceptor.setAccessible(true);
                System.out.println("环绕通知和切点:" + o);
                System.out.println("\t切点为:" + methodMatcher.get(o));
                System.out.println("\t通知为:" + interceptor.get(o));
            }else {
                System.out.println("普通环绕通知:" + o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
} 
  

结果:

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

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

class InterceptorAndDynamicMethodMatcher {
    // 环绕通知
    final MethodInterceptor interceptor;
    // 切点
    final MethodMatcher methodMatcher;

    public InterceptorAndDynamicMethodMatcher(MethodInterceptor interceptor, MethodMatcher methodMatcher) {
        this.interceptor = interceptor;
        this.methodMatcher = methodMatcher;
    }
}

总结:

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

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

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

你可能感兴趣的:(spring系列原理,spring原理)