JAVA && Spring && SpringBoot2.x — 學習目錄
在項目中允趟,我們可以將BeanPostProcessor注冊到IOC容器中涣楷,有什么注意事項呢?
1. 問題起源:
在shiro中,若配置LifecycleBeanPostProcessor
后弧蝇,可能會導致一些類不能支持事務等代理功能去枷。
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
注:LifecycleBeanPostProcessor
是Bean的后置處理器,shiro去控制Bean的生命周期碾阁。但不推薦在配置中加入上述配置罪帖,因為Bean的生命周期一般由Spring去控制。不推薦交由shiro進行控制邮屁!
事務失效場景復現(xiàn):
@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();
}
}
之后整袁,我們發(fā)現(xiàn)ShiroFilterFactoryBean
依賴的Bean,事務等功能全部失效(即依賴的Bean不是代理對象)佑吝。
2. 問題分析
BeanPostProcessor本質(zhì)上也是一個Bean對象坐昙,但是它的初始化時機會早于普通Bean對象。實際上Bean的生命周期大體可分為:
- new 對象(反射)芋忿。
- 屬性依賴注入炸客。
- 調(diào)用生命周期回調(diào)方法疾棵。
- 完成AOP代理。
而實際上無論是屬性的依賴注入
還是完成AOP代理
實際上均是在后置處理器BeanPostProcessor
中進行統(tǒng)一處理的痹仙。
若普通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
,為什么還會出現(xiàn)上述問題鞍恢?
3. 問題原因
在Spring源碼中傻粘,注冊BeanPostProcessor
時,會調(diào)用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
創(chuàng)建的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
調(diào)用getBean()
方法時,會將ShiroFilterFactoryBean
以及其依賴的屬性
進行初始化充边,失去同級別的后置處理器的處理庸推。
總結(jié):
BeanPostProcessor
將@Configuration
容器過早的初始化。于是在注冊后置處理器浇冰,會調(diào)用getBean()
導致FactoryBean
過早被初始化予弧。
4. 問題復現(xiàn)
自定義一個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 構造函數(shù)...");
}
@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 初始化...");
}
//低優(yōu)先級
@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;
}
}
執(zhí)行結(jié)果
UserServiceBean 的構造方法.. (使用@Service注解FactoryBean)
aConfig 構造函數(shù)... (帶有后置處理器的@Configuration容器港柜,容器先初始化请契,才會初始化后置處理器。)
MyBeanPostProcessor 初始化... (PriorityOrdered級別的后置處理器)
AccountImpl 的構造方法... (FactoryBean所依賴的普通Bean夏醉,accountImpl是方法參數(shù)爽锥,先初始化它,才能進行FactoryBean的構造)
PersonServiceBean 構造方法... (FactoryBean的構造方法畔柔。)
bConfig 構造方法... (普通的@Configuration容器)
SomeServiceBean 的構造方法 (普通容器中的FactoryBean方法)
AcService 的構造方法... (帶有@Service的普通Bean)
可以看到氯夷,MyBeanPostProcessor
導致了aConfig
過早的初始化。在注冊Ordered級別的BeanPostProcessor
時靶擦,遍歷所有的BeanDefinition
對象腮考,導致aConfig
中PersonServiceBean
被初始化。而PersonServiceBean
在初始化之前玄捕,要先初始化方法參數(shù)踩蔚,即將AccountImpl
提前初始化。
而實際上枚粘,若是@Service
注冊的FactoryBean
寂纪,其依賴的屬性AcService
也不會過早的被初始化。
5. 歸納總結(jié)
我們可以先看下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.<Integer>getNumber("order"));
return bpp;
}
}
將后置處理器單獨放在了一個容器中柬姚。
總結(jié):
BeanPostProcessor
的注冊需要注意時機拟杉,防止將@Configuration
容器中過早的初始化,造成一些Bug量承。根據(jù)源碼來講搬设,一般推薦兩種方式:
- 將
BeanPostProcessor
單獨的放在一個@Configuration
容器中; - 若與其他Bean共用一個
@Configuration
容器撕捍,推薦使用static方法注冊后置處理器
拿穴;