博客 / 詳情

返回

spring6-bean的生命週期與循環依賴

bean的生命週期

粗略劃分5步

  1. 實例化bean

調用的是無參數的構造方法

  1. bean屬性賦值

執行set注入

  1. 初始化bean

調用bean的init()方法,需要自己寫,自己配

  1. 使用bean

  2. 銷燬bean

調用bean的destroy(),需要自己寫,自己配

注意:自定義的init()和destroy()需要在配置文件配置

<!--    init-method指定初始化方法,destroy-method指定銷燬方法-->
<!--    這兩個方法需要在bean類中定義-->
    <bean id="user" class="com.ali.bean.User" init-method="initBean" destroy-method="destroyBean"></bean>

進一步七步

在以上的5步中,第三步是初始化bean。其實可以在初始化之前和初始化之後添加代碼。此時,需要加入“Bean後處理器”。

編寫一個類實現BeanPostProcessor類,並重寫before和after方法

  1. 實例化bean

調用的是無參數的構造方法

  1. bean屬性賦值

執行set注入

  1. 執行“Bean後處理器”的before方法
  2. 初始化bean

調用bean的init()方法,需要自己寫,自己配

  1. 執行“Bean後處理器”的after方法

  2. 使用bean

  3. 銷燬bean

// 日誌類bean後處理器
public class LogBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor befor方法");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("BeanPostProcessor after方法");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
<!--  配置bean後處理器
  這個bean後處理器會在bean實例化後和初始化前後執行相應的方法
   例如可以在bean初始化前後打印日誌
   需要實現BeanPostProcessor接口
   這樣spring容器在創建其他bean時會自動調用這個後處理器的相應方法
  例如上面的springBean在實例化和初始化時就會調用LogBeanPostProcessor中的方法
   這樣就可以在控制枱看到日誌輸出
  這個bean後處理器必須配置在spring配置文件中,才能被spring容器識別和調用
  這個bean後處理器將作用於當前配置文件中的所有bean
  -->
  <bean class="com.ali.bean.LogBeanPostProcessor" />

精細化分為10步

  1. 實例化bean

調用的是無參數的構造方法

  1. bean屬性賦值

執行set注入

  1. 檢查bean是否實現Aware相關接口(BeanNameAware, BeanClassLoaderAware, BeanFactoryAware),如果實現了,則調用這些接口相關的方法。

  2. 執行“Bean後處理器”的before方法

  3. 檢查bean是否實現InitializingBean接口,並調用接口方法

  4. 初始化bean

調用bean的init()方法,需要自己寫,自己配

  1. 執行“Bean後處理器”的after方法

  2. 使用bean

  3. 檢查bean是否實現了DisposableBean接口,並調用接口方法

  4. 銷燬bean

調用bean的destroy(),需要自己寫,自己配

spring容器只對singleton的bean進行完整的生命週期管理。

如果是prototype作用域的bean,spring容器只負責初始化完畢,等客户端程序一旦獲取到該bean後,spring容器就不再管理該對象的聲明週期了。

自己new的對象如何讓spring容器管理

使用DefaultListableBeanFactory類注入自己創建的對象。

public static void main(String[] args) {
    User user = new User();
    System.out.println(user);
    // 將以上new的對象交給spring容器管理
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    beanFactory.registerSingleton("userBean", user);
    // 從spring容器中獲取
    Object userBean = beanFactory.getBean("userBean");
    System.out.println(userBean);
}

bean的循環依賴問題

bean的循環依賴:A對象中有B屬性。B對象中有A屬性。這種你依賴我,我依賴你的情況就是循環依賴。

singleton+setter模式

<!--    singleton+setter方式解決循環依賴-->
<!--     通過setter方法注入依賴,可以解決循環依賴問題 -->
<!--     spring容器在創建bean時會先實例化bean對象,然後通過setter方法注入依賴-->
<!--     這樣即使存在循環依賴,spring也能正確創建和注入bean-->
    <bean id="husbandBean" class="com.ali.bean.Husband" >
        <property name="name" value="Jack"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <bean id="wifeBean" class="com.ali.bean.Wife" >
        <property name="name" value="Rose"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
在這種模式(singleton+setter)下主要分為2個階段來解決:
   1. 在spring容器加載的時候,實例化bean。只要其中任意一個bean實例化之後,馬上進行“曝光”【不等屬性賦值就曝光】
   2. bean“曝光”後再進行賦值

prototype+setter模式

<!--    在prototype+setter方式下無法解決循環依賴問題,會出現異常
    因為prototype作用域下,spring容器不會緩存bean實例,每次獲取都會創建一個新的實例
    這樣當husbandBean實例化時,wifeBean還沒有被創建,導致無法注入wifeBean
    反之亦然,最終會導致循環依賴失敗,拋出異常
    所以prototype作用域下不支持循環依賴
    但是:當2個bean的scope不同時(其中任意一個是singleton)是可以的
    例如下面的配置中,husbandBean是singleton作用域,wifeBean是prototype作用域
    這樣在創建husbandBean時,wifeBean會被創建並注入
    但是每次獲取wifeBean時,都會創建一個新的實例
    這樣就避免了循環依賴的問題
    因為singleton只會創建一次,而prototype每次獲取都會創建新的實例
    所以這種組合方式是可行的-->

    <bean id="husbandBean" class="com.ali.bean.Husband" scope="singleton">
        <property name="name" value="Jack"/>
        <property name="wife" ref="wifeBean"/>
    </bean>
    <bean id="wifeBean" class="com.ali.bean.Wife"  scope="prototype">
        <property name="name" value="Rose"/>
        <property name="husband" ref="husbandBean"/>
    </bean>
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.