Spring选择代理

jdk和cglib的统一

1. spring的代理选择规则

1.1 两种代理

  1. aspect: 多个通知和多个切面
  2. advisor:一个通知和一个切面

以advisor为例,一个增强包括以下几步骤:

  1. pointcut——定义切点
  2. advice——定义通知(增强)
  3. advisor——创建切面、创建target对象
  4. 创建并执行代理

1.2 切点(org.springframework.aop.Pointcut):用于筛选匹配,匹配成功后增强

  1. 切点实现类:org.springframework.aop.aspectj.AspectJExpressionPointcut
  2. 切点实现类:org.springframework.aop.support.annotation.AnnotationMatchingPointcut

1.3 通知advice(org.aopalliance.intercept.MethodInterceptor 注意,不是spring的cglib的MethodInterceptor):用于功能增强

1.4 切面advisor:org.springframework.aop.support.DefaultPointcutAdvisor

  1. new DefaultPointcutAdvisor(pointcut,advice);
  2. new Target()

1.5 创建并执行代理:

   // 4. 创建代理
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target1);
        proxyFactory.addAdvisors(advisor);
        proxyFactory.setInterfaces(I1.class); // 设置接口类型
        I1 proxy =(I1) proxyFactory.getProxy();
        // 查看代理类型
        System.out.println(proxy.getClass());
        proxy.foo();
        proxy.bar();

1.6 代理选择

  1. proxyTargetClass = false, 目标实现了接口,用jdk实现
  2. proxyTargetClass = false, 目标没有实现接口,用cglib
  3. proxyTargetClass = true, 总是使用cglib

2. 底层的切点实现

2.1 切点匹配


/**
 * @author ssozh
 * @data 2022/12/30
 */
public class A16 {
    public static void main(String[] args) throws NoSuchMethodException {
        /**
         * case 1 根据方法名进行匹配
         */
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* bar())");
        System.out.println(pointcut.matches(T1.class.getMethod("foo"), T1.class));// 切点匹配 , 要判断的方法是? 目标类是?
        System.out.println(pointcut.matches(T1.class.getMethod("bar"), T1.class));// 匹配成功 返回true

        /**
         * case2 根据annotation进行匹配
         */
        AspectJExpressionPointcut pointcut2 = new AspectJExpressionPointcut();
        pointcut2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
        System.out.println(pointcut2.matches(T1.class.getMethod("foo"), T1.class)); // true
        System.out.println(pointcut2.matches(T1.class.getMethod("bar"), T1.class)); // false

        /**
         * case3
         */
        StaticMethodMatcherPointcut pointcut3 = new StaticMethodMatcherPointcut() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                // 先看方法上是不是有 注解
                MergedAnnotations annotations = MergedAnnotations.from(method);
                if (annotations.isPresent(Transactional.class)) {
                    return true;
                }
                // 第二个参数 默认使用 DIRECT 不会查看实现接口上是否包含该注解
                annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
                return annotations.isPresent(Transactional.class);
            }
        };
        System.out.println(pointcut3.matches(T1.class.getMethod("foo"), T1.class)); // true
        System.out.println(pointcut3.matches(T1.class.getMethod("bar"), T1.class)); // false
        System.out.println(pointcut3.matches(T2.class.getMethod("bar"), T2.class)); // true
        System.out.println(pointcut3.matches(T3.class.getMethod("foo"), T3.class)); // true


    }

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

        }

        public void bar() {

        }
    }

    /**
     * 加在类上,这个类里面所有的方法都需要事务增强
     */
    @Transactional
    static class T2 {
        public void foo() {

        }

        public void bar() {

        }
    }

    @Transactional
    interface I3 {
        void foo();
    }

    /**
     * 接口实现方法也可以实现事务加强
     */
    static class T3 implements I3 {

        @Override
        public void foo() {

        }
    }
}

3. 底层的通知实现

4. 底层的切面实现

4.1 高级的@Aspect切面和低级的Advisor切面

  1. 给代理类添加@Aspect注解,并给通知增加相关注解(@Before、@After等)
  2. ApplicationContext注入BeanPostProcessor=>AnnotationAwareAspectJAutoProxyCreator
  3. AnnotationAwareAspectJAutoProxyCreator对context容器中的Bean对象进行增强
    3.1 通过AbstractAutoProxyCreator接口实现的getAdvicesAndAdvisorsForBean方法( findEligibleAdvisors) 找到切面
    3.2 通过org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary 对找到的切面创建代理
package org.springframework.aop.framework.autoproxy;

/**
 * @author ssozh
 * @data 2022/12/30
 */
public class A17 {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean("config", Config.class);
        context.registerBean("aspect1", Aspect1.class);
        context.registerBean(ConfigurationClassPostProcessor.class);
        // 重点:实现了一个BeanPostProcessor接口:创建-> (创建代理)  -> 依赖注入 —> 初始化 (或者创建代理)
        // org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors => 寻找切面
        // org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary => 创建代理
        context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

        // 初始化容器
        context.refresh();

        AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
        // 第一个重要方法:findEligibleAdvisors 根据目标类型 返回和该目标类型匹配的所有切面,比如
        // 用于org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.getAdvicesAndAdvisorsForBean
        List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");
//        List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2"); // 这个返回的advisors为空
        for (Advisor advisor : advisors) {
            System.out.println(advisor);
        }
        // 第二个重要方法:wrapIfNecessary 根据第一个方法返回的集合,如果不为空,则对目标类进行wrap并返回代理对象(执行这个方法 不需要先执行findEligibleAdvisors)
        Object proxy1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
        Object proxy2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
        System.out.println(proxy2.getClass()); // 是本身:A17$Target2
        System.out.println(proxy1.getClass()); // 是代理:class A17$Target1$$EnhancerBySpringCGLIB$$91cd2035
        ((Target1) proxy1).foo();
        ((Target2) proxy2).bar();

//        Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);


    }


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

    static class Target2 {
        public void bar() {
            System.out.println("Target2 exec 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 before... ");
        }
    }

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

        @Bean
        public MethodInterceptor advice3() {
            return invocation -> {
                System.out.println("advice3 before..");
                Object result = invocation.proceed();
                System.out.println("advice3 after..");
                return result;
            };
        }
    }
}

4.2 代理生成时机

代理创建时机:
a. 代理的创建时机

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

b. 依赖注入与初始化不应该被增强,仍赢被施加于初始对象

具体case:

  1. 不存在循环依赖的case:
    1.1 执行Bean1的构造
    1.2 执行Bean1的初始化含税
    1.3 执行Bean1的增强
    1.4 Bean2初始化
    1.5 注入Bean1的增强后对象

  2. 存在循环依赖的case:
    2.1 初始化Bean1
    2.2 初始化Bean2
    2.3 创建Bean1代理
    2.4 Bean2依赖注入Bean1
    2.5 初始化Bean2
    2.6 Bean1依赖注入Bean2
    2.6 初始化Bean1

4.3 吐槽切面的顺序控制

  1. 对于高级切面,可以使用@Order注解来排序,数字小的优先级高。
  2. 对于低级切面DefaultPointAdvisor实现了Order接口,可以setOrder

吐槽:

  1. @Order注解 加在@Aspect的对应的类上有效,加在@Before和@After这类的方法上是无效的
  2. @Order注解 加在@Bean后面也无效

4.4 高级切面转低级切面(@Before注解解析过程)

@Before 前置通知会被转化为下面元素的AspectJMethodBeforeAdvice 形式,该对象包含了如下信息:

  1. 通知代码从哪来
  2. 切点是什么?这里为啥要切点
  3. 通知对象如何创建,本例共用一个Aspect 对象

类似的通知还有:

  1. AspectJAroundAdvice (环绕通知)
  2. AspectJAfterAdvice()
  3. AspectJAfterReturningAdvice
  4. AspectJAfterThrowingAdvice
public class A17_2 {

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

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

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

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

    /**
     * @param args
     */
    public static void main(String[] args) {

        List<Advisor> advisorList = new ArrayList<>();

        // 创建工厂
        AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());


        // 假设知道切面类为Aspect->知道方法-
        for (Method method : Aspect.class.getDeclaredMethods()) {

            // 1. 检查是否标注了xx注解,然后获取切点
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            if (method.isAnnotationPresent(Before.class)) {
                // 根据Before的值生成一个切点对象
                String expr = method.getAnnotation(Before.class).value();
                pointcut.setExpression(expr);
            }

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

            // 3. 解析低级切面
            Advisor advisor = new DefaultPointcutAdvisor(pointcut,advice);

            // 4. 收集切面集合
            advisorList.add(advisor);
        }
        advisorList.forEach(System.out::println);
    }
}
posted @ 2023-01-19 19:24  SsoZh  阅读(71)  评论(0编辑  收藏  举报