Spring 错误的使用BeanFactoryPostProcessor

在公司内,Spring基本都是首选的IOC框架,Spring也提供了很多扩展点让我们介入到容器的生命周期中,例如BeanFactoryPostProcessor、BeanPostProcessor等。今天就记录下BeanFactoryPostProcessor的一种不正确用法。

1. 约定

实例化:instantiation
初始化:initialization

2. BeanFactoryPostProcessor的作用

首先贴下BeanFactoryPostProcessor的源码吧

1
2
3
4
public interface BeanFactoryPostProcessor {

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

首先请一字一句通读一下doc,其中两句话非常重要:

  • BeanFactoryPostProcessor允许使用者修改容器中的bean definitions
  • BeanFactoryPostProcessor可以与bean definitions打交道,但是千万不要进行bean实例化(感觉这里应该说的是不要在BeanFactoryPostProcessor进行可能触发bean实例化的操作)。这么做可能会导致bean被提前实例化,会破坏容器造成预估不到的副作用。如果你需要hack到bean实例化过程,请考虑使用BeanPostProcessor。

从doc中可以读到,BeanFactoryPostProcessor的主要作用是让你能接触到bean definitions,对bean definitions进行一定hack,但是也仅此而已了。绝对不允许在BeanFactoryPostProcessor中触发到bean的实例化!!! 为啥呢,doc说得很清楚but never bean instances. Doing so may cause premature bean instantiation, violating the container and causing unintended side-effects. 下面就列举错误使用造成的两种典型“副作用”。

3. 副作用1——使用注解进行依赖注入失败

贴一下示例代码片段吧。

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
29
30
31
32
33
34
35
36
37
38
@Component
public class PrematureBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Map<String, BBean> map = beanFactory.getBeansOfType(BBean.class);
for (BBean bBean : map.values()) {
assert bBean.getABean() == null;
}
}
}

@Component("bBean")
public class BBean {

@Autowired
private ABean aBean;

public ABean getABean() {
return aBean;
}

}

@Component
public class ABean {

private String name = "a";

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

如上demo所示,在运行后,BBean中被期待注入的ABean最终为null。这是为啥呢?

贴一段ApplicationContext的启动代码吧

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步表示
prepareRefresh();
//告诉子类启动refreshBeanFactory()方法,bean定义资源文件的载入从子类的refreshBeanFactory()方法启动
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//为BeanFactory配置容器特性,例如类加载器,事件处理器等
//准备在上下文中使用的bean工厂
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);

try {
//为容器的某些子类指定特殊的BeanPost事件处理器
// Allows post-processing of the bean factory in context subclasses. 允许在上下文子类中对bean工厂进行后处理。
postProcessBeanFactory(beanFactory);

// 调用所有注册的BeanFactoryPostProcessor的bean
// Invoke factory processors registered as beans in the context. 在上下文中调用注册为bean的工厂处理器。
invokeBeanFactoryPostProcessors(beanFactory);

//为BeanFactory注册BeanPost事件处理器。
//BeanPostProcessor是bean后置处理器,用于监听容器触发的事件
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);

//初始化资源信息,和国际化相关
// Initialize message source for this context.
initMessageSource();

//初始化容器事件传播器
// Initialize event multicaster for this context.
initApplicationEventMulticaster();

//调用子类的某些特殊Bean初始化方法
// Initialize other special beans in specific context subclasses.
onRefresh();

//为事件传播器注册事件监听器
// Check for listener beans and register them.
registerListeners();

//初始化所有剩余的单列bean
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);

//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
// Last step: publish corresponding event.
finishRefresh();
}

catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}

//销毁已创建的bean
// Destroy already created singletons to avoid dangling resources.
destroyBeans();

// 取消refresh操作,重置容器的同步标识
// Reset 'active' flag.
cancelRefresh(ex);

// Propagate exception to caller.
throw ex;
}

finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

可以看到的是postProcessBeanFactory(beanFactory); 首先invoke了容器中的BeanFactoryPostProcessor实现类,其中当然就包括PrematureBeanFactoryPostProcessor,此时通过beanFactory.getBeansOfType触发了bean提前实例化。按理说,bean提前实例化也应该没问题的,aBean也应该是能够被注入的呀!那为啥最终不是这个结果呢。让我们研究下@Resource @AutoWired这种注解是如何注入依赖的,如何起作用的就明白了。@AutoWired起作用依赖AutowiredAnnotationBeanPostProcessor,@Resource依赖CommonAnnotationBeanPostProcessor,这俩都是BeanPostProcessor的实现。那BeanPostProcessors在何处被spring invoke呢,参见registerBeanPostProcessors(beanFactory);postProcessBeanFactory(beanFactory); 后面被调用,也就是说BBean被触发提前初始化的时候,AutowiredAnnotationBeanPostProcessor还没有被注册自然也不会被执行到,自然ABean=null

4. 副作用2——可能会将ApplicationContext容器启动过程暴露在多线程之下

“将ApplicationContext容器启动过程暴露在多线程之下”倒不完全是因为错误使用BeanFactoryPostProcessor,而是错上加错。假设我们发现在副作用1的场景下aBean无法注入,而将BBean通过如下方式修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Component("bBean")
public class BBean implements ApplicationContextAware {

private ApplicationContext context;

@Autowired
private ABean aBean;

public ABean getABean() {
return (ABean)context.getBean("aBean");
}

@Override
public String toString() {
return "BBean{" +
"aBean=" + aBean +
'}';
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}

这样问题就更大了,虽然在大多数的场景下不会有问题。但是如果BBean是类似于像DTS processer bean或者像是MetaQ的 listener bean情况就非常糟糕了。这种bean一旦被实例化+初始化之后就可以接受请求,如果在请求处理代码中触发了Spring bean的实例化过程,则此时的容器会被破坏,结果是无法预知的。而且问题和可能不太容易复现,因为只有在spring启动过程中请求进来触发ABean首次实例化才有可能会发生错误。强烈要求MetaQ listener DTSprocesser等bean依赖容器自身的依赖注入,不要认为干扰;如果实在要用listener or processor 中使用context.getBean(“aBean”)的方式,那也请做好listener or processor 的生命周期管理,保证在容器彻底起来之后才开始处理请求

5. 总结

本文列举了BeanFactoryPostProcessor错误使用可能造成的问题。认真读doc,共勉!

原文出自:https://www.jianshu.com/p/3d099ea43b0e
参考:https://www.cnblogs.com/quanyongan/p/4133725.html