寫在前面
在前面的文章中,我們講述了BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法在bean初始化的前後調用,我們可以自定義類來實現BeanPostProcessor接口,並在postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法中指定我們自定義的邏輯。今天,我們來一起探討下eanPostProcessor底層原理。
項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
bean的初始化和銷燬
我們知道BeanPostProcessor的postProcessBeforeInitialization()方法在bean的初始化之前調用;而postProcessAfterInitialization()方法在bean初始化的之後調用。而bean的初始化和銷燬方法我們可以通過如下方式進行指定。
1.通過@Bean指定init-method和destroy-method
@Bean(initMethod="init",destroyMethod="detory")
public Car car(){
return new Car();
}
2.通過讓Bean實現InitializingBean(定義初始化邏輯)
@Component
public class Cat implements InitializingBean,DisposableBean {
public Cat(){
System.out.println("cat constructor...");
}
@Override
public void destroy() throws Exception {
System.out.println("cat...destroy...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("cat...afterPropertiesSet...");
}
}
3.可以使用JSR250
- @PostConstruct:在bean創建完成並且屬性賦值完成;來執行初始化方法。
- @PreDestroy:在容器銷燬bean之前通知我們進行清理工作。
@Component
public class Dog implements ApplicationContextAware {
//@Autowired
private ApplicationContext applicationContext;
public Dog(){
System.out.println("dog constructor...");
}
//對象創建並賦值之後調用
@PostConstruct
public void init(){
System.out.println("Dog....@PostConstruct...");
}
//容器移除對象之前
@PreDestroy
public void detory(){
System.out.println("Dog....@PreDestroy...");
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
4.可以使用BeanPostProcessor
/**
* 後置處理器:初始化前後進行處理工作
* 將後置處理器加入到容器中
* 在bean初始化前後進行一些處理工作;
* postProcessBeforeInitialization:在初始化之前工作
* postProcessAfterInitialization:在初始化之後工作
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor,Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean);
return bean;
}
@Override
public int getOrder() {
return 3;
}
}
通過這幾種方式,我們就可以對bean的整個生命週期進行控制:
- 從bean的實例化:調用bean的構造方法,我們可以在bean的無參構造方法中執行相應的邏輯。
- bean的初始化:在初始化時,可以通過BeanPostProcessor的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法進行攔截,執行自定義的邏輯;通過@PostConstruct註解、InitializingBean和init-method來指定bean初始化前後執行的方法,執行自定義的邏輯。
- bean的銷燬:可以通過@PreDestroy註解、DisposableBean和destroy-method來指定bean在銷燬前執行的方法,指執行自定義的邏輯。
所以,通過上述方式,我們可以控制Spring中bean的整個生命週期。
BeanPostProcessor源碼解析
如果想深刻理解BeanPostProcessor的工作原理,那就不得不看下相關的源碼,我們可以在MyBeanPostProcessor類的postProcessBeforeInitialization()方法和postProcessAfterInitialization()方法中打上斷點來進行調試。如下所示。
隨後,我們以Debug的方式來運行BeanLifeCircleTest類的testBeanLifeCircle04()方法,運行後的效果如下所示。
可以看到,程序已經運行到MyBeanPostProcessor類的postProcessBeforeInitialization()方法中,在IDEA的左下角我們可以清晰的看到方法的調用棧,如下所示。
通過這個方法調用棧,我們可以詳細的分析從運行BeanLifeCircleTest類的testBeanLifeCircle04()方法開始,到進入MyBeanPostProcessor類的postProcessBeforeInitialization()方法的執行流程。只要我們在IDEA的方法調用棧中找到BeanLifeCircleTest類的testBeanLifeCircle04()方法,依次分析方法調用棧中在BeanLifeCircleTest類的testBeanLifeCircle04()方法上面位置的方法,即可瞭解整個方法調用棧的過程。要想定位方法調用棧中的方法,只需要在IDEA的方法調用棧中單擊相應的方法即可。
注意:方法調用棧是先進後出的,也就是說,最先調用的方法會最後退出,每調用一個方法,JVM會將當前調用的方法放入棧的棧頂,方法退出時,會將方法從棧頂的位置彈出。有關方法調用的具體細節內容,後續會在【JVM】專欄詳細介紹,這裏,小夥伴們就先了解到此即可。
接下來,我們在IDEA的方法調用棧中,找到BeanLifeCircleTest類的testBeanLifeCircle04()方法並單擊,此時IDEA的主界面會定位到BeanLifeCircleTest類的testBeanLifeCircle04()方法,如下所示。
在BeanLifeCircleTest類的testBeanLifeCircle04()方法中,首先通過new實例對象的方式創建了一個IOC容器。接下來,通過IDEA的方法調用棧繼續分析,接下來,進入的是AnnotationConfigApplicationContext類的構造方法。
在AnnotationConfigApplicationContext類的構造方法中會調用refresh()方法。我們跟進方法調用棧,如下所示。
可以看到,方法的執行定位到AbstractApplicationContext類的refresh()方法中的如下代碼行。
finishBeanFactoryInitialization(beanFactory);
這行代碼的作用就是:初始化所有的(非懶加載的)單實例bean對象。
我們繼續跟進方法調用棧,如下所示。
此時,方法的執行定位到AbstractApplicationContext類的finishBeanFactoryInitialization()方法的如下代碼行。
beanFactory.preInstantiateSingletons();
這行代碼的作用同樣是:初始化所有的(非懶加載的)單實例bean。
我們繼續跟進方法調用棧,如下所示。
可以看到,方法的執行定位到DefaultListableBeanFactory的preInstantiateSingletons()方法的最後一個else分支調用的getBean()方法上。繼續跟進方法調用棧,如下所示。
此時方法定位到AbstractBeanFactory類中的getBean()方法中,在getBean()方法中,又調用了doGetBean()方法,通過方法調用棧我們可以得知方法的執行定位到AbstractBeanFactory類中的doGetBean()方法的如下代碼段。
可以看到,在Spring內部是通過getSingleton()來獲取單實例bean的,我們繼續跟進方法調用棧,如下所示。
此時,方法定位到了DefaultSingletonBeanRegistry了類的getSingleton()方法的如下代碼行。
singletonObject = singletonFactory.getObject();
繼續跟進方法調用棧,如下所示。
此時,方法會定位到AbstractBeanFactory類的doGetBean()方法中的如下代碼行。
return createBean(beanName, mbd, args);
也就是說,當第一次獲取單實例bean時,由於單實例bean還未創建,Spring會調用createBean()方法來創建單實例bean。繼續跟進方法調用棧,如下所示。
可以看到,方法的執行定位到AbstractAutowireCapableBeanFactory類的createBean()方法的如下代碼行。
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
可以看到,Spring中創建單實例bean調用的是doCreateBean()方法。沒錯,繼續跟進方法調用棧,如下所示。
方法的執行已經定位到AbstractAutowireCapableBeanFactory類的doCreateBean()方法的如下代碼行。
exposedObject = initializeBean(beanName, exposedObject, mbd);
繼續跟進方法調用棧,如下所示。
方法的執行定位到AbstractAutowireCapableBeanFactory類的initializeBean()方法的如下代碼行。
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
小夥伴們需要重點留意一下這個applyBeanPostProcessorsBeforeInitialization()方法。回過頭來我們再來看AbstractAutowireCapableBeanFactory類的doCreateBean()方法中的如下代碼行。
exposedObject = initializeBean(beanName, exposedObject, mbd);
沒錯,在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中調用的initializeBean()方法中調用了後置處理器的邏輯。小夥伴們需要注意一下,在AbstractAutowireCapableBeanFactory類的doCreateBean()方法中調用的initializeBean()方法之前,調用了一個populateBean()方法,代碼行如下所示。
populateBean(beanName, mbd, instanceWrapper);
我們點到這個populateBean()方法中,看下這個方法執行了哪些邏輯,如下所示。
populateBean()方法同樣是AbstractAutowireCapableBeanFactory類中的方法,populateBean()方法的代碼比較多,其實邏輯非常簡單,populateBean()方法做的工作就是爲bean的屬性賦值。也就是說,在Spring中會先調用populateBean()方法爲屬性賦好值,然後再調用initializeBean()方法。接下來,我們好好分析下initializeBean()方法,爲了方便,我將Spring中AbstractAutowireCapableBeanFactory類的initializeBean()方法的代碼拿出來,如下所示。
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
在initializeBean()方法中,調用了invokeInitMethods()方法,代碼行如下所示。
invokeInitMethods(beanName, wrappedBean, mbd);
invokeInitMethods()方法的作用就是:執行初始化方法,這些初始化方法包括我們之前講的: 在xml文件中的標籤中使用init-method屬性指定的初始化方法;在@Bean註解中使用initMehod屬性指定的方法;使用@PostConstruct註解標註的方法;實現InitializingBean接口的方法等。
在調用invokeInitMethods()方法之前,Spring調用了applyBeanPostProcessorsBeforeInitialization()方法,代碼行如下所示。
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
在調用invokeInitMethods()方法之後,Spring調用了applyBeanPostProcessorsAfterInitialization()方法,如下所示。
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
這裏,我們先來看看applyBeanPostProcessorsBeforeInitialization()方法中具體執行了哪些邏輯,applyBeanPostProcessorsBeforeInitialization()方法位於AbstractAutowireCapableBeanFactory類中,源碼如下所示。
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
可以看到,在applyBeanPostProcessorsBeforeInitialization()方法中,會遍歷所有BeanPostProcessor對象,執行所有BeanPostProcessor對象的postProcessBeforeInitialization()方法,一旦BeanPostProcessor對象的postProcessBeforeInitialization()方法返回null,則後面的BeanPostProcessor對象不再執行,直接退出for循環。
看Spring源碼,我們看到一個細節, 在Spring中調用initializeBean()方法之前,調用了populateBean()方法來爲bean的屬性賦值。
我們將關鍵代碼的調用過程使用如下僞代碼表述出來。
populateBean(beanName, mbd, instanceWrapper);
initializeBean(beanName, exposedObject, mbd){
applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
invokeInitMethods(beanName, wrappedBean, mbd);
applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
也就是說,在Spring中,調用initializeBean()方法之前,調用了populateBean()方法爲bean的屬性賦值,爲bean的屬性賦好值之後,再調用initializeBean()方法進行初始化。
在initializeBean()中,調用自定義的初始化方法invokeInitMethods()之前,調用了applyBeanPostProcessorsBeforeInitialization()方法,而在調用自定義的初始化方法invokeInitMethods()之後,調用了applyBeanPostProcessorsAfterInitialization()方法。整個bean的初始化過程就結束了。
好了,咱們今天就聊到這兒吧!別忘了給個在看和轉發,讓更多的人看到,一起學習一起進步!!
項目工程源碼已經提交到GitHub:https://github.com/sunshinelyz/spring-annotation
寫在最後
如果覺得文章對你有點幫助,請微信搜索並關注「 冰河技術 」微信公衆號,跟冰河學習Spring註解驅動開發。公衆號回覆“spring註解”關鍵字,領取Spring註解驅動開發核心知識圖,讓Spring註解驅動開發不再迷茫。