Spring Bean 生命週期之“我從哪裡來?” 懂得這個很重要

tan日拱一兵發表於2019-07-05

Spring bean 的生命週期很容易理解。例項化 bean 時,可能需要執行一些初始化以使其進入可用 (Ready for Use)狀態。類似地,當不再需要 bean 並將其從容器中移除時,可能需要進行一些清理,這就是它的生命週期

frog-758072_1920.png

上一篇文章 面試還不知道BeanFactory和ApplicationContext的區別? 中說明了介面 Beanfactory 和 Applicationcontext 可以通過 T getBean(String name, Class<T> requiredType) 方法從 Spring 容器中獲取bean,區別是,前者是懶載入形式,後者是預載入的形式。那麼問題來了:

這些 Spring Beans 是怎麼生成出來的呢?

在正式回答這個問題之前,先解答一些有關 Java Bean, Spring Bean 和 Spring IoC 容器這些概念性的疑惑,我希望通過下面這個例子形象說明這些問題:

小學生 (Java Bean)通過提交資料申請(後設資料配置)加入了少先隊(Spring Ioc 容器),學習了一些精神與規定之後,變成了少先隊員(Spring Bean)

從這裡可以看出,Java Bean 和 Spring Bean 都是具有特定功能的物件,小學生還是那個小學生,只不過加入了少先隊之後有了新的身份,新的身份要按照組織 (Spring Ioc)的規定履行特定義務

來看下圖加深一下了解
Xnip2019-07-01_20-14-05.jpg

首先要有容器,例項化 Spring Ioc 容器是非常簡單的,介面 org.springframework.context.ApplicationContext 表示Spring IoC容器,負責例項化,配置和組裝上述 bean。 容器通過讀取配置後設資料獲取有關要例項化,配置和組裝的物件的指令。 配置後設資料通常以XML,Java 註解或程式碼的形式表示。 它允許你自己表達組成應用程式的物件以及這些物件之間豐富的相互依賴性,比如這樣:

ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"spring.xml", "spring1.xml"});

有了容器,我們需要做哪些處理,使其內部物件變為 Ready for Use 的狀態?
我們需要通過 Spring 容器例項化它們,Spring 為我們提供了三種方式:

三種初始化方式

InitializingBean

Spring 為我們提供了 InitializingBean 介面

public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

我們可以通過實現 InitializingBean 介面,在其唯一方法 afterPropertiesSet 內完成例項化的工作,但是 Spring Framework 官方並不建議我們通過這種方法來完成 Bean 的例項化,這是一種強耦合的方式,我們看到框架層面才會用到這個方法。

@PostConstruct

這種方式是 Spring 非常提倡的一種方式,我們通常將其標記在方法上即可,通常習慣將這個方法起名為 init()

@PostConstruct
public void init() {
  System.out.println("Inside init() method...");
}

init-method

你應該見過這種初始化方式:

public class MyClass {
   public void init() {
      // perform post-creation logic here
   }
}

@Configuration
public class AppConfig {
   @Bean(initMethod = "init")
   public MyClass myclass() {
      return new MyClass ();
   }
}

你也應該見過這種配置方式:

<bean id="myClass" class="com.demo.MyClass" init-method="init"/>

沒錯,這只是同樣功能的不同實現方式罷了
以上就是三種初始化 Spring Beans 的方式,我們在框架中看到過三種方式在組合使用,那麼組合使用的呼叫順序是什麼呢?

  1. 首先@PostConstruct 會被最先呼叫
  2. 其次 InitializingBean.afterPropertiesSet() 方法將會被呼叫
  3. 最後呼叫通過 XML 配置的 init-method 方法或通過設定 @Bean 註解 設定 initMethod 屬性的方法

瞭解了這些,你也就瞭解了 Spring Bean 是怎麼來的了

通過圖示來說明一下:
Xnip2019-07-01_21-46-24.jpg

組合shying,這個呼叫順序很難記憶嗎嗎?

PostConstruct (P),afterPropertiesSet (A),init-method (I) ---> PAI (圓周率π)

BeanPostProcessor

BeanPostProcessor 介面,大家也應該有印象,裡面只有兩個方法:

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object var1, String var2) throws BeansException;

    Object postProcessAfterInitialization(Object var1, String var2) throws BeansException;
}

看方法名,BeforeInitialization 和 AfterInitialization,我們應該猜得出,這是在上述三種方式的前和後,算是一種全域性的切面思想,我們經常會使用 postProcessAfterInitialization 方法,通過讀取 Bean 的註解完成一些後續邏輯編寫與屬性的設定,現在 Ready for Use之前是這樣:
Xnip2019-07-02_08-38-03.jpg

Ready for Use 之前,瞭解這些內容,已可以基本滿足日常的工作內容,但這並不是 Ready for Use 的全部內容,Spring Bean 整個生命週期的流程應該是這樣的,後續文章會逐步點亮:
Xnip2019-07-02_08-48-24.jpg

靈魂追問

  1. 瞭解了 Spring Bean 是怎麼來的?那它是怎麼沒的呢?什麼時候需要銷燬他們呢?
  2. Spring 框架中 XxxxAware,這些類有什麼作用,能在 Ready for Use 之前有用處嗎?
  3. 你日常的工作中有充分利用今天說明的這些內容嗎?懂得這些會大大方便你的程式設計

補充說明

  1. 雖然當下流行以註解宣告方式進行程式設計,甚至高版本 Spring 會將一些方法標記為過時,但文章說明依舊會使用 XMLBeanFactory 這類方法,包括 XML 配置。這樣做,只不過為了更清晰的說明問題。
  2. 另外將 Spring Bean 宣告週期的講解,進行拆分,是為了讓大家有獨立的思考空間,帶著問題去思考、時間,而不是被動的填充,最終串聯起自己的學習網路,這樣理解的更深刻,具體請看之前寫的文章 程式猿為什麼要看原始碼, 後續內容請持續關注

歡迎持續關注公眾號:「日拱一兵」,後續會出一系列文章點亮 Spring Bean 週期圖,以完整程式碼施力說明這個週期的順序;同時進行 Spring 知識點解釋與串聯,輕鬆搞定面試那點事,以及在工作中充分利用 Spring 的特性

推薦閱讀


a (1).png

相關文章