JAVA && Spring && SpringBoot2.x — 学习目录
在项目中,我们可以将BeanPostProcessor注册到IOC容器中,有什么注意事项呢?
1. 问题起源:
在shiro中,若配置LifecycleBeanPostProcessor
后,可能会导致一些类不能支持事务等代理功能。
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
注:LifecycleBeanPostProcessor
是Bean的后置处理器,shiro去控制Bean的生命周期。但不推荐在配置中加入上述配置,因为Bean的生命周期一般由Spring去控制。不推荐交由shiro进行控制!
事务失效场景复现:
@Configuration
public class ShiroConfig {
//配置过滤器链
@Bean("shiroFilterBean")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager,JwtAuthcFilter jwtAuthcFilter,JwtPermissionFilter jwtPermissionFilter,Ini.Section section) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
...
return shiroFilterFactoryBean;
}
//配置shiro生命周期后处理器
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
}
之后,我们发现ShiroFilterFactoryBean
依赖的Bean,事务等功能全部失效(即依赖的Bean不是代理对象)。
2. 问题分析
BeanPostProcessor本质上也是一个Bean对象,但是它的初始化时机会早于普通Bean对象。实际上Bean的生命周期大体可分为:
- new 对象(反射)。
- 属性依赖注入。
- 调用生命周期回调方法。
- 完成AOP代理。
而实际上无论是属性的依赖注入
还是完成AOP代理
实际上均是在后置处理器BeanPostProcessor
中进行统一处理的。
若普通Bean初始化时,BeanPostProcessor
没有全部初始化,那么可能会造成普通Bean对象功能上的缺失。在Spring启动过程中,会抛出如下警告:
//没有得到所有后置处理器的处理,例如缺失了自动代理后置处理器处理。
Bean 'accountImpl' of type [com.tellme.Impl.AccountImpl] is not eligible for getting
processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
但是有些同学会问,在自定义BeanPostProcessor
时依赖普通Bean,可能会造成自动代理失效。但"问题起源"中仅仅引入了shiro定义的LifecycleBeanPostProcessor
,为什么还会出现上述问题?
3. 问题原因
在Spring源码中,注册BeanPostProcessor
时,会调用getBean()方法获取Bean对象。
//源码位置:org.springframework.context.support.PostProcessorRegistrationDelegate#registerBeanPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, org.springframework.context.support.AbstractApplicationContext)
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
在获取Bean对象时,遍历所有的BeanDefinition
,来获取对应的Bean。若容器中存在FactoryBean
时,会检验FactoryBean
创建的Bean类型,而不是FactoryBean
的类型。故检测FactoryBean
类型时,可能会将普通Bean
实例化。
//核心源码:
1. org.springframework.beans.factory.support.DefaultListableBeanFactory#doGetBeanNamesForType
2. org.springframework.beans.factory.support.AbstractBeanFactory#isTypeMatch(java.lang.String, org.springframework.core.ResolvableType)
而LifecycleBeanPostProcessor
是在@Configuration
中进行注册的。并且注册时@Configuration
容器中也包含了一个FactoryBean
对象(ShiroFilterFactoryBean)
。
这样会导致注册Ordered
级别的BeanPostProcessor
调用getBean()
方法时,会将ShiroFilterFactoryBean
以及其依赖的属性
进行初始化,失去同级别的后置处理器的处理。
总结:
BeanPostProcessor
将@Configuration
容器过早的初始化。于是在注册后置处理器,会调用getBean()
导致FactoryBean
过早被初始化。
4. 问题复现
自定义一个PriorityOrdered级别的后置处理器,启动项目,查看Bean的初始化时机。
-
UserServiceBean
是FactoryBean
并使用@Service
注册到容器;
@Service
public class UserServiceBean implements FactoryBean {
@Autowired
private AcService acService; //包含一个普通Bean,查看给普通Bean的初始化时机
public UserServiceBean() {
System.out.println("UserServiceBean 的构造方法..");
}
@Override
public Object getObject() throws Exception {
return "";
}
@Override
public Class> getObjectType() {
return Object.class;
}
}
-
aConfig
是一个@Configuration
类,其中包含了
2.1PersonServiceBean
一个FactoryBean;
2.2MyBeanPostProcessor
是一个PriorityOrdered
级别的后置处理器;
2.3AcService
一个普通的Service类;
@Configuration
public class aConfig {
public aConfig() {
System.out.println("aConfig 构造函数...");
}
@Bean
public PersonServiceBean personService(IAccount account) {
PersonServiceBean personServiceBean = new PersonServiceBean();
personServiceBean.setAccount(account);
return personServiceBean;
}
@Bean
public MyBeanPostProcessor myBeanPostProcessor() {
return new MyBeanPostProcessor();
}
}
//后置处理器
public class MyBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
public MyBeanPostProcessor() {
System.out.println("MyBeanPostProcessor 初始化...");
}
//低优先级
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
//FactoryBean类
public class PersonServiceBean implements FactoryBean {
private IAccount account;
public IAccount getAccount() {
return account;
}
public PersonServiceBean() {
System.out.println("PersonServiceBean 构造方法...");
}
public void setAccount(IAccount account) {
this.account = account;
}
@Override
public Object getObject() throws Exception {
return "";
}
@Override
public Class> getObjectType() {
return Object.class;
}
}
-
bConfig
是一个@Configuration
类,包含了一个SomeServiceBean
;
//FactoryBean类
public class SomeServiceBean implements FactoryBean {
public SomeServiceBean() {
System.out.println("SomeServiceBean 的构造方法");
}
@Override
public Object getObject() throws Exception {
return "";
}
@Override
public Class> getObjectType() {
return Object.class;
}
}
执行结果
UserServiceBean 的构造方法.. (使用@Service注解FactoryBean)
aConfig 构造函数... (带有后置处理器的@Configuration容器,容器先初始化,才会初始化后置处理器。)
MyBeanPostProcessor 初始化... (PriorityOrdered级别的后置处理器)
AccountImpl 的构造方法... (FactoryBean所依赖的普通Bean,accountImpl是方法参数,先初始化它,才能进行FactoryBean的构造)
PersonServiceBean 构造方法... (FactoryBean的构造方法。)
bConfig 构造方法... (普通的@Configuration容器)
SomeServiceBean 的构造方法 (普通容器中的FactoryBean方法)
AcService 的构造方法... (带有@Service的普通Bean)
可以看到,MyBeanPostProcessor
导致了aConfig
过早的初始化。在注册Ordered级别的BeanPostProcessor
时,遍历所有的BeanDefinition
对象,导致aConfig
中PersonServiceBean
被初始化。而PersonServiceBean
在初始化之前,要先初始化方法参数,即将AccountImpl
提前初始化。
而实际上,若是@Service
注册的FactoryBean
,其依赖的属性AcService
也不会过早的被初始化。
5. 归纳总结
我们可以先看下SpringBoot2.x如何去注册后置处理器的。
- 注册@Validated后置处理器
源码:org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration
public class ValidationAutoConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(Validator.class)
public static LocalValidatorFactoryBean defaultValidator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
@Bean
@ConditionalOnMissingBean
public static MethodValidationPostProcessor methodValidationPostProcessor(
Environment environment, @Lazy Validator validator) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
boolean proxyTargetClass = environment
.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
processor.setProxyTargetClass(proxyTargetClass);
processor.setValidator(validator);
return processor;
}
}
若MethodValidationPostProcessor
和其他Bean在一个容器中,那么注册时使用static方法。
- 注册@Async 后置处理器:
源码:org.springframework.scheduling.annotation.ProxyAsyncConfiguration
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
bpp.configure(this.executor, this.exceptionHandler);
Class extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
bpp.setAsyncAnnotationType(customAsyncAnnotation);
}
bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
bpp.setOrder(this.enableAsync.getNumber("order"));
return bpp;
}
}
将后置处理器单独放在了一个容器中。
总结:
BeanPostProcessor
的注册需要注意时机,防止将@Configuration
容器中过早的初始化,造成一些Bug。根据源码来讲,一般推荐两种方式:
- 将
BeanPostProcessor
单独的放在一个@Configuration
容器中; - 若与其他Bean共用一个
@Configuration
容器,推荐使用static方法注册后置处理器
;