【探索Spring底层】12.谈谈代理创建器与代理创建时机 末蓝、 2024-03-30 09:45 67阅读 0赞 #### 文章目录 #### * 1. 前言 * 2. 谈谈代理创建器 * 3. 代理创建时机是什么时候 * 4. 浅谈@Order的失效场景 * 4. 浅谈@Order的失效场景 * 5. 高级切面如何转为低级切面 ## 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][image-20221216162324165 1] 这里给高级切面设置优先级为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); } } -------------------- [image-20221216153452748]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/30/9a02d16d2c464a76a9fe850aed38e2aa.png [image-20221216153510617]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/30/764a734f6aae419d9fbce947b18cb840.png [image-20221216154000264]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/30/47a1d2aa44e74beb8199629cdffd1167.png [image-20221216155742002]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/30/d8a5b84696fc4f21971864b4385d189d.png [image-20221216160850682]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/30/e43afdbbaa3744d5948fafe62fb3cb6b.png [image-20221216161303301]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/30/c8e5e813d2024a44ad9e7164277bd424.png [image-20221216162324165]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/30/4044fa98d4dc469d82ba52e35ff1a67d.png [image-20221216162324165 1]: https://image.dandelioncloud.cn/pgy_files/images/2024/03/30/5f1b7aa736314f1d9c14cd141f0854f5.png
相关 【探索Spring底层】8.手撕jdk代理原理与cglib代理原理 文章目录 【探索Spring底层】手撕jdk代理原理与cglib代理原理 1. 前言回顾 2. 手撕jdk代理 3. jdk反射优化 4. 骑猪看日落/ 2024年04月06日 13:20/ 0 赞/ 66 阅读
相关 【探索Spring底层】10.Spring选择代理 文章目录 【探索Spring底层】Spring选择代理 1. 前言回顾 2. 切面的概念 3. Spring选择代理 【探索Spring底层】 ゝ一世哀愁。/ 2024年04月03日 06:58/ 0 赞/ 91 阅读
相关 【探索Spring底层】14.谈谈参数解析器 文章目录 1. 参数解析器概述 2. 常见参数的解析 1. 参数解析器概述 参数解析器是Spring-Web包提供的组件,并且SpringMVC中提供 拼搏现实的明天。/ 2024年03月30日 11:46/ 0 赞/ 60 阅读
相关 【探索Spring底层】12.谈谈代理创建器与代理创建时机 文章目录 1. 前言 2. 谈谈代理创建器 3. 代理创建时机是什么时候 4. 浅谈@Order的失效场景 4. 浅谈@Order的失效场景 末蓝、/ 2024年03月30日 09:45/ 0 赞/ 68 阅读
相关 Spring源码解析——创建AOP代理之获取增强器 正文 在上一篇的博文中我们讲解了通过自定义配置完成了对AnnotationAwareAspectJAutoProxyCreator类型的自动注册,那么这个类到底做了什么工作来 电玩女神/ 2024年03月01日 08:42/ 0 赞/ 41 阅读
相关 WebLogic创建代理服务器 创建代理服务器,用户通过代理服务器访问项目 示意图: ![这里写图片描述][SouthEast] 创建代理服务器: ![这里写图片描述 不念不忘少年蓝@/ 2022年06月09日 13:26/ 0 赞/ 415 阅读
相关 Spring自动创建代理--BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator 前面的文章中讲到的创建代理,在配置文件中都必须配置:ProxyFactoryBean。这虽然省掉了我们手动实现动态代理的工作,但是也很繁琐。 现在有三种解决方案: 1:Be 柔情只为你懂/ 2022年05月18日 00:54/ 0 赞/ 235 阅读
相关 Spring AOP代理创建解析 准备 Spring版本:5.0.8 代理创建过程解析 根据上文解析,来到 `AbstractAutoProxyCreator->wrapIfNecessar 刺骨的言语ヽ痛彻心扉/ 2022年05月14日 08:28/ 0 赞/ 219 阅读
相关 Spring创建对象的时机 转载自 一、单例管理的对象: 1. 默认情况下,Bean的作用域默认为单例类型。spring在读取xml文件的时候,就会创建对象。 2. 在创建的对象的时候(先 左手的ㄟ右手/ 2021年12月17日 11:53/ 0 赞/ 272 阅读
还没有评论,来说两句吧...