简介
本文介绍BeanPostProcessor为什么无法使用AOP以及其解决方案。
原因
简述
BeanPostProcessor以及依赖的bean很有可能无法使用AOP。
此时,会有类似这样的打印信息:trationDelegate$BeanPostProcessorChecker : Bean ‘myBean’ of type [com.example.processor.MyBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
源码位置
这个是由BeanPostProcessorChecker类打印出来的,在源码中搜索它,发现此类是PostProcessorRegistrationDelegate的静态内部类。
@Override public Object postProcessAfterInitialization(Object bean, String beanName) { if (!(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) && this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) { if (logger.isInfoEnabled()) { logger.info("Bean '" + beanName + "' of type [" + bean.getClass().getName() + "] is not eligible for getting processed by all BeanPostProcessors " + "(for example: not eligible for auto-proxying)"); } } return bean; }
打印此日志的原因
若注册到beanFactory的BeanPostProcessor的数量少于总的BeanPostProcessor的数量。即:有其他后置处理器还没准备好,这个bean(此处是myBean)就被实例化了。这个bean被实例化的原因一般是:BeanPostProcessor的实现类引用了这个bean,导致这个bean的实例化。
官网文档
Spring官方文档的原话:
BeanPostProcessor instances and AOP auto-proxying
Classes that implement the BeanPostProcessor interface are special and are treated differently by the container. All BeanPostProcessor instances and beans that they directly reference are instantiated on startup, as part of the special startup phase of the ApplicationContext. Next, all BeanPostProcessor instances are registered in a sorted fashion and applied to all further beans in the container. Because AOP auto-proxying is implemented as a BeanPostProcessor itself, neither BeanPostProcessor instances nor the beans they directly reference are eligible for auto-proxying and, thus, do not have aspects woven into them.
For any such bean, you should see an informational log message: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).
翻译:
实现了BeanPostProcessor 接口的类是特殊的,它们会被容器不同地对待。所有的BeanPostProcessor 实例和它们直接引用的beans在启动时被实例化,它们作为ApplicationContext的特殊启动阶段的一部分。然后,所有的BeanPostProcessor 实例都被以一个排序方式注册而且被应用到下一步的beans中。因为Spring的AOP自动代理是通过实现BeanPostProcessor接口来做的,所以BeanPostProcessor的实现类和它们直接引用的bean不满足AOP自动代理的条件,因此,它们无法被织入到AOP中。
对于这些bean,你会看到一些INFO级别的信息:Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying).
问题复现
代码
BeanPostProcessor实现类
刻意实现Ordered接口,模拟MyProcessor的初始化早于其他BeanPostProcessor实现类的情况,否则,不易复现。本处我测试时,将其指定为最高优先级或者最低优先级,效果是一样的。优先级见:Spring-BeanPostProcessor-作用/介绍 – 自学精灵
另外,此处必须实现Ordered接口才能指明顺序,如果使用@Order是无效的。猜测:@Order也是通过实现BeanPostProcessor接口来做的,所以,也无效。
package com.example.processor; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; @Component public class MyProcessor implements BeanPostProcessor, Ordered { @Autowired private MyBean myBean; @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyBean) { System.out.println("postProcessBeforeInitialization==> " + "This is MyBean"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof MyBean) { System.out.println("postProcessAfterInitialization==> " + "This is MyBean"); } return bean; } // 此方法用来测试AOP,作为切点 public void testAOP() { System.out.println("testAOP方法(MyProcessor)"); } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } }
Bean
package com.example.processor; import org.springframework.stereotype.Component; @Component public class MyBean { // 此方法用来测试AOP,用作切点 public void testAOP() { System.out.println("testAOP方法(MyBean)"); } }
切面
package com.example.processor; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; @Component @Aspect public class BeanPostProcessorAspect { // 此方法织入PostBean的testAOP方法 @Before("execution(* com.example.processor.MyProcessor.testAOP(..))") public void before() { System.out.println("before MyProcessor#testAOP"); } // 此方法织入MyBean的testAOP方法 @Before("execution(* com.example.processor.MyBean.testAOP(..))") public void before2() { System.out.println("before MyBean#testAOP"); } }
ApplicationContextHolder工具类
package com.example.processor; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext context; public void setApplicationContext(ApplicationContext context) throws BeansException { ApplicationContextHolder.context = context; } public static ApplicationContext getContext() { return context; } }
测试类
package com.example.controller; import com.example.processor.MyBean; import com.example.processor.MyProcessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @Autowired MyProcessor myProcessor; @Autowired MyBean myBean; @GetMapping("/test1") public String test1() { myProcessor.testAOP(); myBean.testAOP(); return "test1 success"; } // @GetMapping("/test1") // public String test1() { // MyProcessor myProcessor = ApplicationContextHolder.getContext().getBean(MyProcessor.class); // myProcessor.testAOP(); // MyBean myBean = ApplicationContextHolder.getContext().getBean(MyBean.class); // myBean.testAOP(); // return "test1 success"; // } }
测试
1.复现无法AOP的现象
启动:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.0.RELEASE) 2021-03-06 12:23:36.622 INFO 14368 --- [ main] com.example.DemoApplication : Starting DemoApplication on DESKTOP-QI6B9ME with PID 14368 (E:\work\Idea_proj\demo_JAVA\demo_SpringBoot\target\classes started by Liu in E:\work\Idea_proj\demo_JAVA\demo_SpringBoot) 2021-03-06 12:23:36.625 INFO 14368 --- [ main] com.example.DemoApplication : No active profile set, falling back to default profiles: default 2021-03-06 12:23:37.237 INFO 14368 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'myBean' of type [com.example.processor.MyBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2021-03-06 12:23:37.492 INFO 14368 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2021-03-06 12:23:37.500 INFO 14368 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-03-06 12:23:37.500 INFO 14368 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.35] 2021-03-06 12:23:37.590 INFO 14368 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2021-03-06 12:23:37.590 INFO 14368 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 927 ms 2021-03-06 12:23:37.718 INFO 14368 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-03-06 12:23:37.847 INFO 14368 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2021-03-06 12:23:37.855 INFO 14368 --- [ main] com.example.DemoApplication : Started DemoApplication in 1.553 seconds (JVM running for 2.454)
访问:http://localhost:8080/test1
结果:(BeanPostProcessor实现类注入的普通bean和BeanPostProcessor实现类都未被AOP)说明:使用Controller中的第二种方法也是同样的结果。
testAOP方法(MyProcessor) testAOP方法(MyBean)
2.去掉BeanPostProcessor即可AOP
将 BeanPostProcessor实现类的implements BeanPostProcessor去掉,并去掉其覆写的方法。(其他都不变,比如:仍然实现Ordered接口)
结果为:(BeanPostProcessor实现类注入的普通bean和BeanPostProcessor实现类都被AOP)
before MyProcessor#testAOP testAOP方法(MyProcessor) before MyBean#testAOP testAOP方法(MyBean)
源码追踪
本处追踪上边“1.复现无法AOP的现象”的代码,入口:MyBean被实例化的源码
bean的实例化入口都是:AbstractBeanFactory#doGetBean,在这个地方打个条件断点:
启动项目,发现这个断点到达了三次,也就是说,有三个地方想实例化MyBean。
第一次:BeanPostProcessor实现类引用了MyBean
第二次:HelloController引用了MyBean
第三次:MyBean本身加入容器,当然要去实例化它
解决方案
方案1:延迟初始化
代码
BeanPostProcessor的实现类中这样引入MyBean:
@Lazy @Autowired private MyBean myBean;
测试
启动时:
postProcessBeforeInitialization==> This is MyBean postProcessAfterInitialization==> This is MyBean
访问:http://localhost:8080/test1
结果(BeanPostProcessor实现类注入的普通bean可以AOP,BeanPostProcessor实现类未被AOP)
testAOP方法(MyProcessor) before MyBean#testAOP testAOP方法(MyBean)
方案2:ApplicationContext
当然,此法在本处不好应用。可以应用在配置Shiro时注入Service。
private UserService getUserService() { return (UserService) applicationContext.getBean(UserService.class); }
请先
!