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


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM