Java開發學習(五)----bean的生命週期

2022-06-16 06:00:22

一、什麼是生命週期

  • 首先理解下什麼是生命週期?

    • 從建立到消亡的完整過程,例如人從出生到死亡的整個過程就是一個生命週期。

  • bean生命週期是什麼?

    • bean物件從建立到銷燬的整體過程。

  • bean生命週期控制是什麼?

    • 在bean建立後到銷燬前做一些事情。

二、環境準備

環境搭建:

  • 建立一個Maven專案

  • pom.xml新增依賴

  • resources下新增spring的組態檔applicationContext.xml

最終專案的結構如下:

(1)專案中新增BookDao、BookDaoImpl、BookService和BookServiceImpl類

public interface BookDao {
    public void save();
}
​
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
}
​
public interface BookService {
    public void save();
}
​
public class BookServiceImpl implements BookService{
    private BookDao bookDao;
​
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
​
    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
    }
}

(2)resources下提供spring的組態檔

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
​
    <bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
</beans>

(3)編寫AppForLifeCycle執行類,載入Spring的IOC容器,並從中獲取對應的bean物件

public class AppForLifeCycle {
    public static void main( String[] args ) {
        ApplicationContext ctx = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) ctx.getBean("bookDao");
        bookDao.save();
    }
}

三、生命週期設定

接下來,在上面這個環境中來為BookDao新增生命週期的控制方法,具體的控制有兩個階段:

  • bean建立之後,想要新增內容,比如用來初始化需要用到資源

  • bean銷燬之前,想要新增內容,比如用來釋放用到的資源

步驟1:新增初始化和銷燬方法

針對這兩個階段,我們在BooDaoImpl類中分別新增兩個方法,方法名任意

public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ...");
    }
    //表示bean初始化對應的操作
    public void init(){
        System.out.println("init...");
    }
    //表示bean銷燬前對應的操作
    public void destory(){
        System.out.println("destory...");
    }
}
步驟2:設定生命週期

在組態檔新增設定,如下:

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>
步驟3:執行程式

執行AppForLifeCycle列印結果為:

從結果中可以看出,init方法執行了,但是destroy方法卻未執行,這是為什麼呢?

  • Spring的IOC容器是執行在JVM中

  • 執行main方法後,JVM啟動,Spring載入組態檔生成IOC容器,從容器獲取bean物件,然後調方法執行

  • main方法執行完後,JVM退出,這個時候IOC容器中的bean還沒有來得及銷燬就已經結束了

  • 所以沒有呼叫對應的destroy方法

知道了出現問題的原因,具體該如何解決呢?

四、close關閉容器

  • ApplicationContext中沒有close方法

  • 需要將ApplicationContext更換成ClassPathXmlApplicationContext

    ClassPathXmlApplicationContext ctx = new 
        ClassPathXmlApplicationContext("applicationContext.xml");
  • 呼叫ctx的close()方法

    ctx.close();
  • 執行程式,就能執行destroy方法的內容

這種方式比較暴力,直接就關閉掉容器了,接下來介紹另一種方式,

五、註冊勾點關閉容器

  • 在容器未關閉之前,提前設定好回撥函數,讓JVM在退出之前回撥此函數來關閉容器

  • 呼叫ctx的registerShutdownHook()方法

  • 是全域性變數,方法放置位置無所謂

    ctx.registerShutdownHook();

    注意:registerShutdownHook在ApplicationContext中也沒有

  • 執行後,查詢列印結果

兩種方式介紹完後,close和registerShutdownHook選哪個?

相同點:這兩種都能用來關閉容器

不同點:close()是在呼叫的時候關閉,registerShutdownHook()是在JVM退出前呼叫關閉。

分析上面的實現過程,會發現新增初始化和銷燬方法,即需要編碼也需要設定,實現起來步驟比較多也比較亂。

Spring提供了兩個介面來完成生命週期的控制,好處是可以不用再進行設定init-methoddestroy-method

接下來在BookServiceImpl完成這兩個介面的使用:

修改BookServiceImpl類,新增兩個介面InitializingBeanDisposableBean並實現介面中的兩個方法afterPropertiesSetdestroy

public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
    public void save() {
        System.out.println("book service save ...");
        bookDao.save(); 
    }
    public void destroy() throws Exception {
        System.out.println("service destroy");
    }
    public void afterPropertiesSet() throws Exception {
        System.out.println("service init");
    }
}

重新執行AppForLifeCycle類

那第二種方式的實現,我們也介紹完了。

小細節

  • 對於InitializingBean介面中的afterPropertiesSet方法,翻譯過來為屬性設定之後

  • 對於BookServiceImpl來說,bookDao是它的一個屬性

  • setBookDao方法是Spring的IOC容器為其注入屬性的方法

  • 思考:afterPropertiesSet和setBookDao誰先執行?

    • 從方法名分析,猜想應該是setBookDao方法先執行

    • 驗證思路,在setBookDao方法中新增一句話

      public void setBookDao(BookDao bookDao) {
              System.out.println("set .....");
              this.bookDao = bookDao;
          }
      ​
    • 重新執行AppForLifeCycle,列印結果如下:

      驗證的結果和我們猜想的結果是一致的,所以初始化方法會在類中屬性設定之後執行。

六、bean生命週期總結

(1)關於Spring中對bean生命週期控制提供了兩種方式:

  • 在組態檔中的bean標籤中新增init-methoddestroy-method屬性

  • 類實現InitializingBeanDisposableBean介面。

(2)對於bean的生命週期控制在bean的整個生命週期中所處的位置如下:

  • 初始化容器

    • 1.建立物件(記憶體分配)

    • 2.執行構造方法

    • 3.執行屬性注入(set操作)

    • 4.執行bean初始化方法

  • 使用bean

    • 1.執行業務操作

  • 關閉/銷燬容器

    • 1.執行bean銷燬方法

(3)關閉容器的兩種方式:

  • ConfigurableApplicationContext是ApplicationContext的子類

    • close()方法

    • registerShutdownHook()方法