說明:本文主要的參考為《精通spring 4.x 企業開發應用實戰》陳雄華 林開雄 文建國 編著
一 理論先行
先看一張圖,也是上面那本書上搬來的
書中把bean的整個生命周期可能會調用的方法分為4類,分別是:
1.Bean自身的方法。比如構造方法,getter setter方法,其他的自定方法
2.Bean級的生命周期方法。要調用這些方法需要由Bean來實現Bean級的生命周期方法接口:比如BeanNameAware,BeanFactoryAware,InitializingBean,DisposableBean,這些接口中的方法是自動調用的,作用范圍只是針對實現了前面所說的4個接口的類型的Bean
3.容器級的生命周期接口方法。圖中帶有☆的方法就是容器級的方法,容器中所有的bean都要被容器級的生命周期方法處理,而且這個級別的接口是單獨實現的,獨立於Bean之外。上圖中的容器及生命周期接口為:InstantiationAwareBeanPostProcessor 和BeanPostProcessor
4.工廠后處理器接口方法:包括AspectJWeavingEnabler,CustomeAutowireConfigurer,ConfigurationClassPostProcessor等方法,在應用上下文裝配文件后立即調用
二 開始實戰
1.先實現Bean的生命周期接口,四個接口,要實現其中的四個方法,實例化和初始化完成后會自動被調用
package com.example.demo.beanlife; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; public class A implements BeanNameAware,BeanFactoryAware,InitializingBean,ApplicationContextAware{ private String name; public A() { System.out.println("A對象正在實例化 name="+ name); } public String getName() { return name; } public void setName(String name) { System.out.println(">>>開始初始化 調用A setName()方法設置對象屬性"); this.name = name; } @Override public void setBeanName(String s) { System.out.println("A實例屬性設置完成,調用setBeanName() beanName=" + s); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("調用 setBeanFactory() 設置beanFactory實例到A對象中"); this.beanFactory = beanFactory; System.out.println("a >>>>>>>>"+ beanFactory.getBean("a").toString()); } @Override
//同一級的生命周期方法中最后一個被調用的,但是只會調用一次,之后在調用bean的setxx()方法更改屬性時將不會再被被調用到 public void afterPropertiesSet() throws Exception { System.out.println("調用afterPropertiesSet() A 對象的屬性設置已經完成了 name=" + name); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("調用setApplicationContext ApplicationContext的值為" + applicationContext.toString()); } }
配置類(重新寫一個類):
@Configuration
public class BeanConfig {
@Bean
public A a(){
A a = new A();
a.setName("007");
return a;
}
-------輸出如下-------------------
A對象正在實例化 name=null
>>>開始初始化 調用A setName()方法設置對象屬性
A實例屬性設置完成,調用setBeanName() beanName=a
調用 setBeanFactory() 設置beanFactory實例到A對象中
b >>>>>>>>com.example.demo.beanlife.B@5cefde48
a >>>>>>>>com.example.demo.beanlife.A@5557431b
調用setApplicationContext ApplicationContext的值為org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@42e20967, started on Sun Aug 23 14:07:04 CST 2020
調用afterPropertiesSet() A 對象的屬性設置已經完成了 name=007
2.實現容器級的生命周期接口 Bean:InstantiationAwareBeanPostProcessor 和 BeanPostProcessor接口
package com.example.demo.beanlife; import org.springframework.beans.BeansException; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter; import org.springframework.stereotype.Component; //如果后處理器沒有以組件的方式加裝到ioc容器中,處理器將不會發生作用 @Component public class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor { //在調用bean的構造方法實例化bean之前,都會先調用這個方法 @Override public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException { System.out.println( beanName + "實例化之前 調用 postProcessBeforeInstantiation()"); return null; } @Override
//bean實例化完成之后,會調用這個方法:注意,根據我的實踐來看,實例化bean的時候會把我們在配置類中指定的屬性設置好之后才會調用這個方法,此時bean對象中的屬性已經喲值了 public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException { if (bean instanceof A){ A a = (A) bean; System.out.println("A實例對象此時的屬性 name = " + a.getName()); } System.out.println( beanName+ "對象實例化完成 調用postProcessAfterInstantiation 對象類型為:" + bean.getClass().getName() ); return false; } }
-----輸出如下-----------------------
a實例化之前 調用 postProcessBeforeInstantiation()
A對象正在實例化 name=null
>>>開始初始化 調用A setName()方法設置對象屬性
>>>開始初始化 調用A setB()方法設置對象屬性
A實例對象此時的屬性 name = 007
a對象實例化完成 調用postProcessAfterInstantiation 對象類型為:com.example.demo.beanlife.A
A實例屬性設置完成,調用setBeanName() beanName=a
調用 setBeanFactory() 設置beanFactory實例到A對象中
b >>>>>>>>com.example.demo.beanlife.B@636cc54d
a >>>>>>>>com.example.demo.beanlife.A@58b2a8bc
調用setApplicationContext ApplicationContext的值為org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@33741750, started on Sun Aug 23 14:31:00 CST 2020
調用afterPropertiesSet() A 對象的屬性設置已經完成了 name=007
package com.example.demo.beanlife; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.stereotype.Component; @Component public class MyBeanProcesssor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("調用postProcessBeforeInitialization() 對"+beanName+"進行加工"); if (bean instanceof A){ A a = (A)bean; a.setName("008"); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("調用postProcessAfterInitialization() 再次獲得"+beanName+"加工機會"); return bean; } }
---------------輸出結果--------------------
a實例化之前 調用 postProcessBeforeInstantiation()
A對象正在實例化 name=null
>>>開始初始化 調用A setName()方法設置對象屬性
>>>開始初始化 調用A setB()方法設置對象屬性
A實例對象此時的屬性 name = 007
a對象實例化完成 調用postProcessAfterInstantiation 對象類型為:com.example.demo.beanlife.A
A實例屬性設置完成,調用setBeanName() beanName=a
調用 setBeanFactory() 設置beanFactory實例到A對象中
b >>>>>>>>com.example.demo.beanlife.B@55e2cac3
a >>>>>>>>com.example.demo.beanlife.A@4aeb6fa7
調用setApplicationContext ApplicationContext的值為org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@7317f8f0, started on Sun Aug 23 14:52:05 CST 2020
調用postProcessBeforeInitialization() 對a進行加工
>>>開始初始化 調用A setName()方法設置對象屬性
調用afterPropertiesSet() A 對象的屬性設置已經完成了 name=008
調用postProcessAfterInitialization() 再次獲得a加工機會
3.工廠后置處理器,這個處理器只會執行一次,而且針對的是容器,開始實例化bean放入Spring-Ioc容器之前,這個方法會被調用
@Component public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException { System.out.println(" 工廠后置處理器 postProcessBeanFactory()"); } }
--------------輸出結果------------------
工廠后置處理器 postProcessBeanFactory()
tomcatServletWebServerFactory實例化之前 調用 postProcessBeforeInstantiation().........實例化其他的bean
總結:雖然在spring中通過xml文件的形式配置裝配bean時,實例化和初始化是涇渭分明的,調用InstantiationAwarePostProcess的 postProcessAfterInstantiation方法是在bean的構造方法被調用之后立即被調用,但是spring-boot中通過配置類裝配bean時,實例化和初始化好像是連接在一起了,構造方法被調用之后緊接着調用setXX()方法,而后在調用postProcessAfterInstantiation()方法。在springBoot中BeanPostPrecessor的postProcessBeforeInitialization()中可以插足bean的初始過程,再一次修改bean的屬性值, 也可以進行其他操作。
spring中的bean是被Ioc容器((BeanFactory)管理的,所以從bean開始實例化的時候,就要接受容器級的針對bean進行處理的后處理器的處理,這個過程是被動的,沒得選,接下來要調用bean自己的方法進行實例化,同時還要受到Bean的生命周期級接口的管理(如果實現了bean級生命周期接口),這是可選的。