简介
本文用实例来介绍Spring的后置处理器(BeanPostProcessor)的一些坑,开发时要注意这些问题。
坑1:依赖的bean不走BeanPostProcessor
简介
在BeanPostProcessor的实现类中,依赖了其他的bean,那么被依赖的bean被创建时,将不会执行它所在的BeanPostProcessor实现类实现的方法。
因为由于BeanPostProcessor的实现类依赖于其他bean,所以这个bean需要在PostBean之前创建完成,这也就意味着在这个bean创建时,BeanPostProcessor的实现类还未初始化完成,所以不会调用它的方法。
以下用实例进行验证
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.stereotype.Component; @Component public class MyProcessor implements BeanPostProcessor { @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; } }
被依赖的Bean
package com.example.processor; import org.springframework.stereotype.Component; @Component public class MyBean { }
测试
启动后,没有相关打印。
若将@Autowired MyBean myBean删掉,则启动之后,会打印出:
. ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.3.0.RELEASE) 2021-03-05 21:39:47.861 INFO 16100 --- [ main] com.example.DemoApplication : Starting DemoApplication on DESKTOP-QI6B9ME with PID 16100 (E:\work\Idea_proj\demo_JAVA\demo_SpringBoot\target\classes started by Liu in E:\work\Idea_proj\demo_JAVA\demo_SpringBoot) 2021-03-05 21:39:47.864 INFO 16100 --- [ main] com.example.DemoApplication : No active profile set, falling back to default profiles: default 2021-03-05 21:39:48.645 INFO 16100 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2021-03-05 21:39:48.652 INFO 16100 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-03-05 21:39:48.652 INFO 16100 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.35] 2021-03-05 21:39:48.730 INFO 16100 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2021-03-05 21:39:48.731 INFO 16100 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 825 ms postProcessBeforeInitialization==> This is MyBean postProcessAfterInitialization==> This is MyBean 2021-03-05 21:39:48.865 INFO 16100 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 2021-03-05 21:39:48.998 INFO 16100 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path '' 2021-03-05 21:39:49.007 INFO 16100 --- [ main] com.example.DemoApplication : Started DemoApplication in 1.557 seconds (JVM running for 2.638)
坑2:无法使用AOP
见:Spring-BeanPostProcessor无法使用AOP-原因/解决方案 – 自学精灵
坑3:注册方式及其限制
简介
如何将BeanPostProcessor注册到Spring容器中?方式主要有如下两种:
- 将其声明在Spring的配置类或xml文件中,作为普通的bean,让ApplicationContext对象去加载它,这样它就被自动注册到容器中了。而且Spring容器会对BeanPostProcessor的实现类做特殊处理,即会将它们挑选出来,在加载其他bean前,优先加载BeanPostProcessor的实现类。
- 使用ConfigurableBeanFactory接口的addBeanPostProcessor方法手动添加。ApplicationContext对象中组合了一个ConfigurableBeanFactory的实现类对象。但这样添加BeanPostProcessor有一些缺点,如下:
- 一创建Spring容器,配置文件中的单例bean就会被加载,此时addBeanPostProcessor方法还没执行,那手动添加的BeanPostProcessor就无法作用于这些bean,所以手动添加的BeanPostProcessor只能作用于延迟加载的bean,或者非单例bean。
- Ordered接口的作用将失效,而是以注册的顺序执行。前面提过,Ordered接口用来指定多个BeanPostProcessor实现的方法的执行顺序。这是Spring官方文档中提到的:While the recommended approach for BeanPostProcessor registration is through ApplicationContext auto-detection (as described above), it is also possible to register them programmatically against a ConfigurableBeanFactory using the addBeanPostProcessor method. This can be useful when needing to evaluate conditional logic before registration, or even for copying bean post processors across contexts in a hierarchy. Note however that BeanPostProcessor s added programmatically do not respect the Ordered interface. Here it is the order of registration that dictates the order of execution. Note also that BeanPostProcessor s registered programmatically are always processed before those registered through auto-detection, regardless of any explicit ordering.
坑4:使用@Bean配置的限制
简介
如果使用Java类的方式配置Spring,并使用@Bean声明一个工厂方法返回bean实例,那么返回值的类型必须是BeanPostProcessor类型,或者等级低于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 { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessBeforeInitialization==> " + beanName); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("postProcessAfterInitialization==> " + beanName); return bean; } @Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } }
在配置类中,声明MyProcessor可以有以下几种方式
@Configuration public class BeanConfig { // 方式1:MyProcessor @Bean public MyProcessor myProcessor() { return new MyProcessor(); } // 方式2:返回值为BeanPostProcessor @Bean public BeanPostProcessor myProcessor() { return new MyProcessor(); } // 方式3:返回值为Ordered @Bean public Ordered postBean() { return new MyProcessor(); } }
以上三种方式都可以让Spring容器创建MyProcessor实例对象,因为MyProcessor实现了BeanPostProcessor和Ordered接口,所以它的对象也是这两种类型的对象。但是需要注意,上面三种方式中,只有第一种和第二种方式,会让Spring容器将MyProcessor当作BeanPostProcessor处理;而第三种方式,则会被当作一个普通Bean处理,实现BeanPostProcessor的两个方法都不会被调用。因为在MyProcessor的继承体系中,Ordered和BeanPostProcessor是同级别的,Spring无法识别出这个Ordered对象,也是一个BeanPostProcessor对象;但是使用MyProcessor却可以,因为MyProcessor类型就是BeanPostProcessor的子类型。所以,在使用@Bean声明工厂方法返回BeanPostProcessor实现类对象时,返回值必须是BeanPostProcessor类型,或者更低级的类型。Spring官方文档中,这一部分的内容如下:
Note that when declaring a BeanPostProcessor using an @Bean factory method on a configuration class, the return type of the factory method should be the implementation class itself or at least the org.springframework.beans.factory.config.BeanPostProcessor interface, clearly indicating the post-processor nature of that bean. Otherwise, the ApplicationContext won’t be able to autodetect it by type before fully creating it. Since a BeanPostProcessor needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.
请先
!