【探索Spring底层】12.谈谈代理创建器与代理创建时机

导读:本篇文章讲解 【探索Spring底层】12.谈谈代理创建器与代理创建时机,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

1. 前言

Spring中有两种切面,一种是@Aspect,另一种是Advisor

其中@Aspect是高级切面,Advisor是低级切面

这里的高级和低级并不代表其功能强弱,而是低级切面比较适合框架内部使用,而高级切面比较适合编码开发使用。因为低级切面的功能比较基本。

@Aspect切面里面可以包含一组或多组通知与切面。

而Advisor仅支持一组通知和切面。

@Aspect虽然是一种高级切面,但是Spring处理这种高级切面的时候,依然会把高级切面转化成低级切面。因为只有转化为低级切面才能被Spring内部所使用。


2. 谈谈代理创建器

代理创建器,指的是AnnotationAwareAspectJAutoProxyCreator类

AnnotationAwareAspectJAutoProxyCreator是用来处理被@AspectJ注解标注的切面类和Spring Advisors的。

这里面有两个比较重要的方法:

  • findEligibleAdvisors:这个方法是用来找有资格的Advisors,这里说的有资格的Advisor一部分是低级切面,一部分是高级切面
  • wrapIfNecessary:其内部调用的findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理

下面测试一下这两个方法。

这里先准备高级切面和低级切面

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

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

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

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

@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 invocation -> {
            System.out.println("advice3 before...");
            Object result = invocation.proceed();
            System.out.println("advice3 after...");
            return result;
        };
    }
}

注意:这两个方法均为protected,因此其他包并不能直接调用这两个方法,可以用反射来调用,但是这里为了简便,因此把包名设置成这个方法的类的所在包名一样

package org.springframework.aop.framework.autoproxy;

image-20221216153452748

image-20221216153510617

编写测试方法

public static void main(String[] args) {
    GenericApplicationContext context = new GenericApplicationContext();
    context.registerBean("aspect1", Aspect1.class);
    context.registerBean("config", Config.class);
    context.registerBean(ConfigurationClassPostProcessor.class);
    context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
    // BeanPostProcessor

    context.refresh();

    /*
       第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisors
         */
    AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
    List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");
    for (Advisor advisor : advisors) {
        System.out.println(advisor);
    }

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

    /*
           第二个重要方法 wrapIfNecessary
         */
    Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
    System.out.println(o1.getClass());
    Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
    System.out.println(o2.getClass());

    ((Target1) o1).foo();

}

image-20221216154000264

creator.findEligibleAdvisors(Target1.class, "target1")

findEligibleAdvisors有两个参数,第一个是目标的类型,第二个参数是这个类在容器中的名字(并不是很重要,随便写就行)

当执行这个方法的时候,就会根据目标的类型与在容器中的每一个Advisors进行对比,将可以的Advisors收集到集合中

这里输出了四个切面

  • org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR:这是是Spring给所有代理都要加的切面
  • org.springframework.aop.support.DefaultPointcutAdvisor:自己编写的低级切面
  • InstantiationModelAwarePointcutAdvisor:剩下两个是高级切面转化后的两个低级切面
creator.wrapIfNecessary(new Target1(), "target1", "target1")

wrapIfNecessary有三个参数

  1. 目标对象,因为现在是框架外部所以需要自己创建,在框架内部的话则是在容器中寻找

image-20221216155742002

输出的结果可见,o1为代理对象而o2不是代理对象。

这里因为如果目标类型为Target2,那么执行了findEligibleAdvisors方法后返回的集合为空,那么调用wrapIfNecessary就不会创建代理类


3. 代理创建时机是什么时候

对于Bean来讲有三个比较重要的时机,那就是创建、依赖注入和初始化

代理创建的时机一般来说在两个位置

  1. 创建之后,依赖注入之前
  2. 初始化之后

两个位置二选一

准备一个案例

public class A17_1 {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(Config.class);
        context.refresh();
        context.close();
    }

    @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 (MethodInvocation invocation) -> {
                System.out.println("before...");
                return invocation.proceed();
            };
        }

        @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()");
        }
        //@Autowired public void setBean2(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()");
        }
    }
}

这个案例有个特点,依赖关系是单向的,也就是Bean2依赖于Bean1

执行测试类可以看出

image-20221216160850682

首先调用了Bean1的构造方法,然后调用了Bean1的初始化方法

接下来创建Bean1的代理对象,也就是在Bean1初始化之后创建了Bean1的初始化对象

然后调用Bean2的构造方法

因为Bean2依赖于Bean,需要设置Bean1,从打印结果不难看出,这里的Bean1是一个增强后的代理方法

最后Bean2初始

如果Bean2也依赖于Bean1,那么会出现什么呢?(也就是循环依赖)

static class Bean1 {
    public void foo() {

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

image-20221216161303301

首先调用了Bean1的构造,按理说就应该调用Bean1的setBean2方法,但是这时候Bean2还没有创建

因此调用了Bean2的构造方法。

接着调用Bean2中的setBean1方法,前面说过这里需要增强后的Bean1代理对象,因此Bean1代理对象的创建应该在调用Bean2中的setBean1方法之前

接下来初始化Bean2

Bean2初始化之后回到Bean1,给Bean1设置Bean2,接下来Bean1初始化

这就是循环依赖的情况下,代理创建的时机了


4. 浅谈@Order的失效场景

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

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

@Configuration
static class Config {
    @Bean // 低级切面
    public Advisor advisor3(MethodInterceptor advice3) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
        //设置切面执行顺序
        advisor.setOrder(2);
        return advisor;
    }
    @Bean
    public MethodInterceptor advice3() {
        return invocation -> {
            System.out.println("advice3 before...");
            Object result = invocation.proceed();
            System.out.println("advice3 after...");
            return result;
        };
    }
}

image-20221216162324165

这里给高级切面设置优先级为1,低级切面设置优先级为2,因此高级切面先完成,低级切面后完成

这里需要注意几个@Order注解失效的情况

  1. 注解加在了错误的位置

    1. @Configuration
      static class Config {
          @Bean // 低级切面
          @Order(2) //不可以加在这里
          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 invocation -> {
                  System.out.println("advice3 before...");
                  Object result = invocation.proceed();
                  System.out.println("advice3 after...");
                  return result;
              };
          }
      }
      
  2. 不可以给高级切面里面的切面进行控制优先级

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

4. 浅谈@Order的失效场景

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

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

@Configuration
static class Config {
    @Bean // 低级切面
    public Advisor advisor3(MethodInterceptor advice3) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
        //设置切面执行顺序
        advisor.setOrder(2);
        return advisor;
    }
    @Bean
    public MethodInterceptor advice3() {
        return invocation -> {
            System.out.println("advice3 before...");
            Object result = invocation.proceed();
            System.out.println("advice3 after...");
            return result;
        };
    }
}

image-20221216162324165

这里给高级切面设置优先级为1,低级切面设置优先级为2,因此高级切面先完成,低级切面后完成

这里需要注意几个@Order注解失效的情况

  1. 注解加在了错误的位置

    1. @Configuration
      static class Config {
          @Bean // 低级切面
          @Order(2) //不可以加在这里
          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 invocation -> {
                  System.out.println("advice3 before...");
                  Object result = invocation.proceed();
                  System.out.println("advice3 after...");
                  return result;
              };
          }
      }
      
  2. 不可以给高级切面里面的切面进行控制优先级

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

5. 高级切面如何转为低级切面

Spring底层是如何将高级切面转成低级切面的呢?请看下面案例

首先准备一个切面

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");
    }

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

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

    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        try {
            System.out.println("around...before");
            return pjp.proceed();
        } finally {
            System.out.println("around...after");
        }
    }
}

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

就以@Before为例

首先遍历Aspect类中所有方法

接着判断方法上是否有@Before注解

如果有,则通过method.getAnnotation(Before.class).value()获取@Before的值

新建一个切点AspectJExpressionPointcut

给这个切点设置表达式pointcut.setExpression(expression);

最后需要一个通知类AspectJMethodBeforeAdvice

新建这个通知类需要三个参数,第一个是方法对象,第二个是切点,第三个是切面实例工厂

切面实例工厂也就是指new SingletonAspectInstanceFactory(new Aspect())

最后new一个低级切面new DefaultPointcutAdvisor(pointcut, advice)

这样高级切面就转为低级切面了;

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

    AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
    // 高级切面转低级切面类
    List<Advisor> list = new ArrayList<>();
    for (Method method : Aspect.class.getDeclaredMethods()) {
        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);
            // 切面
            Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
            list.add(advisor);
        }
    }
    for (Advisor advisor : list) {
        System.out.println(advisor);
    }

}

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/94951.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!