是五月呀!

SpringBoot中BeanPostProcessor注册

在使用SpringBoot集成shiro时,遇到过一个问题,shiro需要注册一个LifecycleBeanPostProcessor,用来调用内部的init()destroy()方法。
一般在Configuration中注册:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Configuration
public class SpringServiceConfig {
@Autowired
private SpringDataSourceConfig springDataSourceConfig;
private SpringRedisConfig springRedisConfig;
@Autowired
public SpringServiceConfig(SpringRedisConfig springRedisConfig) {
this.springRedisConfig = springRedisConfig;
}
//sessionManager securityManager realm...
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
}

在这个Configuration中,使用@Autowired注入会失败!
如果是在属性上使用:

1
2
@Autowired
private SpringDataSourceConfig springDataSourceConfig;

使用时发现springDataSourceConfig为空;
如果用构造器注入:

1
2
3
4
5
6
private SpringRedisConfig springRedisConfig;
@Autowired
public SpringServiceConfig(SpringRedisConfig springRedisConfig) {
this.springRedisConfig = springRedisConfig;
}

会报错:

1
No default constructor found;

网上搜索后找到答案,原因是LifecycleBeanPostProcessor集成了BeanPostProcessor,而在Spring声明周期中,BeanPostProcessorBeanFactoryPostProcessor的初始化是优先于bean的初始化的:

因此,如果一个@Configuration标注的类中返回了BeanPostProcessor或者BeanFactoryPostProcessor,那么无法使用@Autowired进行注入。
@Bean注解的javadoc中也有说明:

BeanFactoryPostProcessor-returning @Bean methods

Special consideration must be taken for @Bean methods that return Spring BeanFactoryPostProcessor (BFPP) types. Because BFPP objects must be instantiated very early in the container lifecycle, they can interfere(干涉) with processing of annotations such as @Autowired, @Value, and @PostConstruct within @Configuration classes. To avoid these lifecycle issues, mark BFPP-returning @Bean methods as static. For example:

1
2
3
4
@Bean
public static PropertyPlaceholderConfigurer ppc() {
// instantiate, configure and return ppc ...
}

By marking this method as static, it can be invoked without causing instantiation of its declaring @Configuration class, thus avoiding the above-mentioned lifecycle conflicts. Note however that static @Bean methods will not be enhanced for scoping and AOP semantics as mentioned above. This works out in BFPP cases, as they are not typically referenced by other @Bean methods. As a reminder, a WARN-level log message will be issued for any non-static @Bean methods having a return type assignable to BeanFactoryPostProcessor.

解决的方案javadoc中也说了,就是给返回BeanPostProcessor或者BeanFactoryPostProcessor的方法使用static修饰。
这样,调用该方法时,不需要实例化外层的容器。

1
2
3
4
@Bean
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}

参考:
Spring Java based configuration with static method
Spring Bean的生命周期