深入學習Spring Bean生命週期
highlight: atom-one-dark theme: vuepress
小知識,大挑戰!本文正在參與“程序員必備小知識”創作活動
Bean的初始化和銷燬
在整個生命週期過程中,我們可以自定義Bean的初始化和銷燬鈎子函數,當Bean的生命週期到達相應的階段的時候,Spring會調用我們自定義的Bean的初始化和銷燬方法。自定義Bean初始化和銷燬方法有多種方式,下面逐一介紹。
@Bean
上一節中介紹了可以在配置類中通過@Bean
註解來註冊Bean,我們也可以通過它來指定Bean的初始化和方法。
為了演示,我們新建一個Spring Boot項目,然後創建一個User
類:
java
public class User {
public User() {
System.out.println("調用無參構造器創建User");
}
public void init() {
System.out.println("初始化User");
}
public void destory() {
System.out.println("銷燬User");
}
}
然後在配置類裏註冊該組件,並指定初始化和銷燬方法:
```java @Configuration public class WebConfig {
@Bean(initMethod = "init", destroyMethod = "destory")
public User user() {
return new User();
}
} ```
其中initMethod = "init"
和destroyMethod = "destory"
與User類裏的init
,destory
方法相對應。
在Spring Boot入口類中測試:
java
// 返回 IOC 容器,使用註解配置,傳入配置類
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
User user = context.getBean(User.class);
// 關閉 IOC 容器
context.close();
啟動項目,觀察控制枱輸出:
從上面的輸出我們看出在容器啟動之前,先調用對象的無參構造器創建對象,然後調用初始化方法,在容器關閉的時候調用銷燬方法。
上面的情況是對於單例而言的,如果組件是多例模式又是什麼情況呢?我們把上面的組件註冊配置改為多例,然後再次啟動項目,觀察控制枱輸出:
在多例模式下,IOC容器啟動的時候並不會去創建對象,而是在每次獲取的時候才會去調用方法創建對象,創建完對象後再調用初始化方法。
但在容器關閉後,Spring並沒有調用相應的銷燬方法,這是因為在多例模式下,容器不會管理這個組件(只負責在你需要的時候創建這個組件),所以容器在關閉的時候並不會調用相應的銷燬方法。
InitializingBean&DisposableBean
除了上面這種方式指定初始化和銷燬方法外,Spring還為我們提供了和初始化,銷燬相對應的接口:
- InitializingBean
接口包含一個afterPropertiesSet
方法,我們可以通過實現該接口,然後在這個方法中編寫初始化邏輯。
- DisposableBean
接口包含一個destory
方法,我們可以通過實現該接口,然後再這個方法中編寫銷燬邏輯。
新建一個類,名稱為Bird
,然後實現這兩個接口:
```java public class Bird implements InitializingBean, DisposableBean { public Bird() { System.out.println("調用無參構造器創建Bird"); }
@Override
public void destroy() {
System.out.println("銷燬Bird");
}
@Override
public void afterPropertiesSet() {
System.out.println("初始化Bird");
}
}
在配置類中註冊這個組件:
java
@Bean
public Bird bird() {
return new Bird();
}
測試一波:
java
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
System.out.println("容器創建完畢");
context.close();
```
啟動項目,觀察控制枱輸出:
@PostConstruct & @PreDestroy
除了上面兩種指定初始化和銷燬方法的方式外,我們還可以使用@PostConstruct
和@PreDestroy
註解修飾方法來指定相應的初始化和銷燬方法。
新建一個類,名稱為Fish:
```java public class Fish { public Fish() { System.out.println("調用無參構造器創建Fish"); }
@PostConstruct
public void init() {
System.out.println("初始化Fish");
}
@PreDestroy
public void destory() {
System.out.println("銷燬Fish");
}
} ```
在配置類中這個組件:
java
@Bean
public Fish fish(){
return new Fish();
}
測試一波:
java
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(WebConfig.class);
System.out.println("容器創建完畢");
context.close();
啟動項目,觀察控制枱輸出:
效果和上面兩種方式一致。
這兩個註解並非Spring提供,而是JSR250規範提供。
BeanPostProcessor
Spring提供了一個BeanPostProcessor
接口,俗稱Bean後置通知處理器,它提供了兩個方法postProcessBeforeInitialization
和postProcessAfterInitialization
。其中postProcessBeforeInitialization
在組件的初始化方法調用之前執行,postProcessAfterInitialization
在組件的初始化方法調用之後執行。它們都包含兩個入參:
- bean:當前組件對象;
- beanName:當前組件在容器中的名稱。
兩個方法都返回一個Object類型,我們可以直接返回當前組件對象,或者包裝後返回。
我們來定義一個BeanPostProcessor
接口的實現類MyBeanPostProcessor
:
java
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName + " 初始化之前調用");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName + " 初始化之後調用");
return bean;
}
}
在配置類中註冊該組件:
java
@Bean
public MyBeanPostProcessor myBeanPostProcessor () {
return new MyBeanPostProcessor();
}
再次啟動項目,觀察控制枱輸出:
總結
主要把握創建過程和銷燬過程這兩個大的方面:
創建過程:首先實例化Bean,並設置Bean的屬性,根據其實現的Aware接口(主要是BeanFactoryAware接口,BeanFactoryAware,ApplicationContextAware)設置依賴信息,接下來調用BeanPostProcess的postProcessBeforeInitialization方法,完成initial前的自定義邏輯;afterPropertiesSet方法做一些屬性被設定後的自定義的事情;調用Bean自身定義的init方法,去做一些初始化相關的工作;然後再調用postProcessAfterInitialization去做一些bean初始化之後的自定義工作。這四個方法的調用有點類似AOP。 此時,Bean初始化完成,可以使用這個Bean了。
銷燬過程:如果實現了DisposableBean的destroy方法,則調用它,如果實現了自定義的銷燬方法,則調用之。