Spring註解版--Bean的生命週期
Spring註解--Bean的宣告週期
最近,學習Spring註解的時候,看完晚上的教學視訊,在此將Spring註解的Bean的生命週期這一章的內容寫下來,方便以後自己會看。
Bean的生命週期
1.Bean的生命週期:
Bean建立-------->Bean初始化-------->Bean銷燬
2.Spring容器來管理Bean的生命週期:
自定義初始化和銷燬的方法:容器在Bean進行到當前生命週期的時候來呼叫我我們自定義的初始化和銷燬方法。
2.1、指定初始化和銷燬方法
- xml檔案的配置方式中,我們可以在
<bean>
標籤中新增init-method
以及destory-method
這兩個屬性,指定初始化和銷燬的方法。 - 註解程式設計中,我們在方法中新增
@Bean
的initMethod
以及destoryMethod
這兩個屬性,指定初始化和銷燬的方法。
2.1.1、建立bean物件
我們在bean檔案包下面建立一個Car類檔案,內部有一個無參構造器、一個初始化方法以及一個銷燬方法。
package com.xiaojian.bean;
public class Car {
public Car(){
System.out.println("car...constructor");
}
public void init(){
System.out.println("car...init");
}
public void destory(){
System.out.println("car...destory");
}
}
2.1.2、建立配置類
我們在config檔案包下面建立一個MainConfig1類檔案,內部有一個bean。
package com.xiaojian.config;
import com.xiaojian.bean.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class MainConfig1 {
// @Scope("prototype") //此為多例項
@Bean(value = "car",initMethod = "init",destroyMethod = "destory")
public Car car(){
return new Car();
}
}
2.1.3、建立測試類測試
我們在test檔案包下面建立MainTest1測試類檔案。
package com.xiaojian.test;
import com.xiaojian.config.MainConfig1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest1 {
public static void main(String[] args) {
//1、建立IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig1.class);
System.out.println("IOC容器建立完成");
Object car = applicationContext.getBean("car");
System.out.println("car的bean建立完成");
//2、關閉容器
applicationContext.close();
System.out.println("IOC容器關閉");
}
}
- 單例項測試結果:
D:\basic\jdk\jdk1.8.0_131\bin\java ...
car...constructor
car...init
IOC容器建立完成
car的bean建立完成
car...destory
IOC容器關閉
Process finished with exit code 0
如上,為控制檯列印的日誌檔案,可以看出來在單例項的情況下,bean物件在IOC容器初始化完成之前就已經建立完成,同時初始化方法init
在bean物件建立完成之後直接執行,等容器關閉的時候destory
方法執行。
- 多例項測試結果:
D:\basic\jdk\jdk1.8.0_131\bin\java ...
IOC容器建立完成
car...constructor
car...init
car的bean建立完成
IOC容器關閉
Process finished with exit code 0
如上,為控制檯列印的日誌檔案,可以看出來在多例項的情況下面,IOC容器建立的時候bean物件並沒有被建立,而是等到我們容器呼叫這個bean的時候才建立這個bean,同時執行init這個初始化方法,但是多例項的容器的銷燬方法不會執行,也就是IOC容器不管理多例項的bean的銷燬。
2.2、實現InitializingBean
(定義初始化邏輯)與DisposableBean
(定義銷燬邏輯)
讓Bean物件實現InitializingBean
介面,重寫它的afterPropertiesSet()
方法;同時讓bean物件實現DisposableBean
介面,重寫它的destroy()
方法來定義銷燬邏輯。
2.2.1、建立bean物件
我們在bean檔案包下面建立一個Cat類檔案,此類實現InitializingBean
、DisposableBean
這兩個介面,並且重寫對應的方法,同時此類本身有一個無參構造器。
package com.xiaojian.bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
//此處使用@Component註解將此bean加入到IOC容器中(也可以在配置類中通過新增@Bean的方式新增)
@Component
public class Cat implements InitializingBean,DisposableBean{
public Cat(){
System.out.println("cat...constructor");
}
@Override
public void destroy() throws Exception {
System.out.println("cat...destory");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("cat...afterPropertiesSet");
}
}
2.2.2、建立配置類
我們在config檔案包下面建立一個MainConfig2類檔案,此配置類中沒有bean物件的方法,而是添加了@ComponentScan
註解來掃描bean物件。
package com.xiaojian.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//通過註解@ComponentScan註解掃描bean檔案包下面的所有配置檔案
@ComponentScan("com.xiaojian.bean")
@Configuration
public class MainConfig2 {
}
2.2.3、建立測試類測試
我們在test檔案包下面建立MainTest2測試類檔案。
package com.xiaojian.test;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest2 {
public static void main(String[] args) {
//1、建立IOC容器
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器建立完成");
System.out.println("IOC容器關閉");
applicationContext.close();
}
}
測試結果:
D:\basic\jdk\jdk1.8.0_131\bin\java ...
cat...constructor
cat...afterPropertiesSet
IOC容器建立完成
IOC容器關閉
cat...destory
Process finished with exit code 0
如上,未控制檯列印的日誌檔案,可以看出來,在IOC容器初始化之前就已經建立好了bean物件,使用了afterPropertiesSet
初始化方法,在IOC容器關閉的時候使用了destory
銷燬方法。
2.3、使用JSR250(在Bean物件的類檔案中標註)
@PostConstruct
:在Bean建立完成並且屬性賦值完成之後來執行初始化方法。@PreDestory
:在容器銷燬Bean之前通知我們進行清理工作。
2.3.1、建立bean物件
我們在bean檔案包下面建立一個Dog類檔案,內部有一個無參構造器,一個init
方法,一個destory
方法。同時init
方法新增上註解@PostConstruct
,destory
方法新增上註解@PreDestory
。
package com.xiaojian.bean;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
//此處使用@Component註解將此bean加入到IOC容器中
// (也可以在配置類中通過新增@Bean的方式新增)
@Component
public class Dog {
public Dog(){
System.out.println("dog...constructor");
}
//物件建立並賦值之後呼叫
@PostConstruct
public void init(){
System.out.println("[email protected]");
}
//在容器銷燬之前呼叫
@PreDestroy
public void destory(){
System.out.println("[email protected]");
}
}
2.3.2、建立配置類
我們在config檔案包下面建立一個MainConfig3類檔案,此配置類中沒有bean物件的方法,而是添加了@ComponentScan
註解來掃描bean物件。
package com.xiaojian.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
//通過註解@ComponentScan註解掃描bean檔案包下面的所有配置檔案
@ComponentScan("com.xiaojian.bean")
@Configuration
public class MainConfig3 {
}
2.3.3、建立測試類測試
我們在test檔案包下面建立MainTest3測試類檔案。
package com.xiaojian.test;
import com.xiaojian.config.MainConfig2;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest3 {
public static void main(String[] args) {
//1、建立IOC容器
AnnotationConfigApplicationContext applicationContext=new AnnotationConfigApplicationContext(MainConfig2.class);
System.out.println("IOC容器建立完成");
System.out.println("IOC容器關閉");
applicationContext.close();
}
}
測試結果:
D:\basic\jdk\jdk1.8.0_131\bin\java ...
dog...constructor
[email protected]
IOC容器建立完成
IOC容器關閉
[email protected]
Process finished with exit code 0
如上,為控制檯列印的日誌檔案,可以看出來,在IOC容器初始化之前就已經建立好了bean物件,使用了@PostConstruct
註解標註的init
初始化方法,在IOC容器關閉的時候使用了@PreDestory
註解標註的destory
銷燬方法。
2.4、BeanPostProcessor
-bean的後置處理器(介面)
在Bean初始化進行前後進行一系列的處理操作:
postProcessBeforeInitialization
:在初始化之前工作postProcessAfterInitialization
:在初始化之後工作
2.4.1、建立實現類
在bean檔案包下面建立MyBeanPostProcessor類檔案,實現BeanPostProcessor
介面。
package com.xiaojian.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* 後置處理器:初始化前後進行處理
* 將後置處理器加入容器中
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization...before..."+beanName+": "+bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization...after..."+beanName+": "+bean);
return bean;
}
}
2.4.2、環境配置
此處的配置類我們用MainConfig3配置類,測試類也用MainTest3測試類,同時我們使用Cat、Dog這兩個bean物件。
2.4.3、執行測試
執行MainTest3測試類,測試結果如下:
D:\basic\jdk\jdk1.8.0_131\bin\java ...
cat...constructor
postProcessBeforeInitialization...before...cat: com.xiaojian.bean.Cat@71d15f18
cat...afterPropertiesSet
postProcessAfterInitialization...after...cat: com.xiaojian.bean.Cat@71d15f18
dog...constructor
postProcessBeforeInitialization...before...dog: com.xiaojian.bean.Dog@2df32bf7
dog...@PostConstruct
postProcessAfterInitialization...after...dog: com.xiaojian.bean.Dog@2df32bf7
IOC容器建立完成
IOC容器關閉
dog...@PreDestory
cat...destory
Process finished with exit code 0
如上,IOC容器中有兩個bean物件,而且在這兩個bean物件的初始化前後都有同一個現象。
在Cat這個bean物件初始化之前,容器先運行了MyBeanPostProcessor中的postProcessBeforeInitialization
方法;初始化之後,容器運行了MyBeanPostProcessor中的postProcessAfterInitialization
方法。(Dog同樣)
3.構造(物件建立)
- 單例項:在容器啟動的時候就建立bean物件
- 多例項:在每次獲取的時候建立bean物件
4.初始化具體過程
下面按照順序的執行:
-
(單例)構造bean
-
BeanPostProcessor.postProcessBeforeInitialization
-
初始化:物件建立完成,並賦值好,之後呼叫方法
-
BeanPostProcessor.postProcessAfterInitialization
-
銷燬:單例項—在容器關閉的時候銷燬(多例項時候容器部管理bean的銷燬)
5、原始碼分析BeanPostProcessor
BeanPostProcessor
遍歷得到容器中所有的BeanPostProcessor
:挨個執行beforeInitialization
,一旦返回null,跳出for迴圈,不會執行後面的BeanPostProcessor.postProcessBeforeInitialization
。
BeanPostProcessor
內部初始化原理:
-
populateBean(beanName,mbd,instanceWrapper)
—給bean進行屬性賦值 -
InitializeBean
的工作順序:applyBeanPostProcessorsBeforeInitialization(wrappedBean,beanName)
;invokeInitMethods(beanName,wrappedBean,mbd)
—執行初始化applyBeanPostProcessorsAfterInitialization(wrappedBean,beanName)
;