1. 程式人生 > >spring4.1.8擴充套件實戰之四:感知spring容器變化(SmartLifecycle介面)

spring4.1.8擴充套件實戰之四:感知spring容器變化(SmartLifecycle介面)

本章由以下幾部分組成:

  1. SmartLifecycle介面概覽;
  2. spring容器啟動與SmartLifecycle的關係;
  3. spring容器關閉與SmartLifecycle的關係;
  4. 關於Lifecycle和SmartLifecycle;
  5. 實戰SmartLifecycle介面擴充套件; SmartLifecycle介面概覽

先來看看SmartLifecycle介面的類圖: 這裡寫圖片描述

如上圖所示,在繼承了Lifecycle和Phased兩個介面後,SmartLifecycle一共定義了六個方法,為了便於後面的原始碼分析,先做個簡介: 方法 作用 start() bean初始化完畢後,該方法會被執行 stop() 容器關閉後: spring容器發現當前物件實現了SmartLifecycle,就呼叫stop(Runnable), 如果只是實現了Lifecycle,就呼叫stop() isRunning() 當前狀態 getPhase() 返回值決定start方法在眾多Lifecycle實現類中的執行順序(stop也是) isAutoStartup() start方法被執行前先看此方法返回值,返回false就不執行start方法了 stop(Runnable) 容器關閉後: spring容器發現當前物件實現了SmartLifecycle,就呼叫stop(Runnable), 如果只是實現了Lifecycle,就呼叫stop()

從上述列舉中可以看出,感知容器變化的能力最終來自Lifecycle,而SmartLifecycle只是Lifecycle的增強版,可以自定義優先順序(getPhase),自主決定是否隨容器啟動(isAutoStartup),以及停止時能接受一個runnable物件(stop(Runnable)); spring容器啟動與SmartLifecycle的關係

現在可以結合spring原始碼來看看SmartLifecycle的使用場景,從spring容器初始化看起;

  1. AbstractApplicationContext類的refresh方法中,在bean的例項化和初始化操作完畢後,會呼叫finishRefresh方法,如下圖紅框所示: 這裡寫圖片描述

  2. finishRefresh方法內容如下,中文註釋對每個方法做了簡介:

protected void finishRefresh() { // LifecycleProcessor例項初始化, // LifecycleProcessor是所有Lifecycle實現類的管家,裡面包含了對Lifecycle的各種操作. initLifecycleProcessor();

// 通過LifecycleProcessor來執行Lifecycle實現類的start方法
getLifecycleProcessor().onRefresh();

// 向監聽器傳送廣播,訊息型別是ContextRefreshedEvent
publishEvent(new ContextRefreshedEvent(this));

// 如果配置了MBeanServer,就完成在MBeanServer上的註冊
LiveBeansView.registerApplicationContext(this);

}

上述程式碼中,initLifecycleProcessor()和getLifecycleProcessor().onRefresh()這兩個方法和本章的主題有關,其他兩個就不在本章展開了,我們從initLifecycleProcessor開始看起吧;

  1. initLifecycleProcessor方法的作用是為applicationContext的成員變數lifecycleProcessor賦值,如果已有名為”lifecycleProcessor”的bean,lifecycleProcessor就等於這個bean,否則就例項化一個DefaultLifecycleProcessor物件,再讓lifecycleProcessor等於這個物件,並且把這個物件作註冊到spring環境中(名為”lifecycleProcessor”),原始碼如下:

protected void initLifecycleProcessor() { ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) { this.lifecycleProcessor = beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class); if (logger.isDebugEnabled()) { logger.debug(“Using LifecycleProcessor [” + this.lifecycleProcessor + “]”); } } else { DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor(); defaultProcessor.setBeanFactory(beanFactory); this.lifecycleProcessor = defaultProcessor; beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor); if (logger.isDebugEnabled()) { logger.debug(“Unable to locate LifecycleProcessor with name '” + LIFECYCLE_PROCESSOR_BEAN_NAME + “’: using default [” + this.lifecycleProcessor + “]”); } } }

  1. 接下來是getLifecycleProcessor().onRefresh()的執行,如果業務不自定義一個LifecycleProcessor,就預設建立一個DefaultLifecycleProcessor物件,因此執行的就是DefaultLifecycleProcessor的onRefresh方法,來看看原始碼:

@Override public void onRefresh() { startBeans(true); this.running = true; }

展開startBeans方法看看,注意入參autoStartupOnly等於true:

private void startBeans(boolean autoStartupOnly) { //取得所有Lifecycle介面的例項,此map的key是例項的名稱,value是例項 Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans(); Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>(); for (Map.Entry<String, ? extends Lifecycle> entry : lifecycleBeans.entrySet()) { Lifecycle bean = entry.getValue(); //autoStartupOnly等於true時,bean必須實現SmartLifecycle介面,並且isAutoStartup()返回true,才會被放入LifecycleGroup中(後續會從LifecycleGroup中取出來執行start()) if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) { int phase = getPhase(bean); LifecycleGroup group = phases.get(phase); if (group == null) { group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly); //phases是個map,key是Lifecycle例項的phase值,value是Lifecycle例項 phases.put(phase, group); } //當前例項加入LifecycleGroup中,該LifecycleGroup內的所有例項的phase都相等 group.add(entry.getKey(), bean); } } if (phases.size() > 0) { List keys = new ArrayList(phases.keySet()); //按照所有的phase值排序,然後依次執行bean的start方法,每次都是一批phase相同的 Collections.sort(keys); for (Integer key : keys) { //這裡面會對所有Lifecycle例項逐個呼叫start方法 phases.get(key).start(); } } }

  1. SmartLifecycle的例項的start被呼叫的地方是在LifecycleGroup內部,對應的方法是doStart,如下所示,優先處理依賴bean:

private void doStart(Map<String, ? extends Lifecycle> lifecycleBeans, String beanName, boolean autoStartupOnly) { Lifecycle bean = lifecycleBeans.remove(beanName); if (bean != null && !this.equals(bean)) { String[] dependenciesForBean = this.beanFactory.getDependenciesForBean(beanName); for (String dependency : dependenciesForBean) { //如果有依賴類,就先呼叫依賴類的start方法,這裡做了迭代呼叫 doStart(lifecycleBeans, dependency, autoStartupOnly); } //條件略多,首先要求isRunning返回false,其次:不能是SmartLifecycle的實現類,若是SmartLifecycle實現類,其isAutoStartup方法必須返回true if (!bean.isRunning() && (!autoStartupOnly || !(bean instanceof SmartLifecycle) || ((SmartLifecycle) bean).isAutoStartup())) { if (logger.isDebugEnabled()) { logger.debug(“Starting bean '” + beanName + “’ of type [” + bean.getClass() + “]”); } try { bean.start(); } catch (Throwable ex) { throw new ApplicationContextException(“Failed to start bean '” + beanName + “’”, ex); } if (logger.isDebugEnabled()) { logger.debug(“Successfully started bean '” + beanName + “’”); } } } }

以上就是初始化階段容器對SmartLifecycle例項的處理邏輯,簡單的小結如下:

  1. Lifecycle的處理都是委託給LifecycleProcessor執行的,先準備好此例項;
  2. 將所有的Lifecycle例項按照phase分組;
  3. 從phase值最小的分組開始,依次執行其中每個Lifecycle物件的start方法;

關於容器啟動時的Lifecycle的處理就分析到這裡,接下來看看容器關閉時對Lifecycle操作; spring容器關閉與SmartLifecycle的關係

分析SmartLifecycle如何感知spring容器的關閉,首先要弄清楚stop方法的呼叫棧,從LifecycleProcessor介面看起吧:

public interface LifecycleProcessor extends Lifecycle {

/**
 * Notification of context refresh, e.g. for auto-starting components.
 */
void onRefresh();

/**
 * Notification of context close phase, e.g. for auto-stopping components.
 */
void onClose();

}

如上所示,感知容器關閉只能靠onClose方法被呼叫了,去看看該方法的呼叫處; 2. LifecycleProcessor的onClose方法是在AbstractApplicationContext的doClose方法中被呼叫的,如下圖紅框所示,這是彙集了容器關閉時要執行的基本邏輯: 這裡寫圖片描述 弄清了呼叫邏輯,可以去DefaultLifecycleProcessor中看看SmartLifecycle例項的stop方法是如何被呼叫的;

  1. DefaultLifecycleProcessor的stop方法中先呼叫stopBeans方法,再將成員變數running設定為false,表示狀態已不是執行中:

@Override public void stop() { stopBeans(); this.running = false; }

  1. 展開stopBeans方法:

private void stopBeans() { //取得所有Lifecycle介面的例項,此map的key是例項的名稱,value是例項 Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans(); Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>(); for (Map.Entry<String, Lifecycle> entry : lifecycleBeans.entrySet()) { Lifecycle bean = entry.getValue(); //SmartLifecycle例項通過getPhase方法返回,只實現了Lifecycle的返回0 int shutdownOrder = getPhase(bean); LifecycleGroup group = phases.get(shutdownOrder); if (group == null) { group = new LifecycleGroup(shutdownOrder, this.timeoutPerShutdownPhase, lifecycleBeans, false); //phases是個map,key是Lifecycle例項的phase值,value是Lifecycle例項 phases.put(shutdownOrder, group); } group.add(entry.getKey(), bean); } if (phases.size() > 0) { List keys = new ArrayList(phases.keySet()); //按照phase排序,和啟動的時候的排序正好相反 Collections.sort(keys, Collections.reverseOrder()); for (Integer key : keys) { //對phase相同的Lifecycle例項,逐一執行stop方法 phases.get(key).stop(); } } }

上述程式碼和啟動時執行start的邏輯基本相似,不同的是執行順序正好相反;

  1. 看看LifecycleGroup的stop方法內部,是如何呼叫Lifecycle例項的stop方法的:

public void stop() { if (this.members.isEmpty()) { return; } if (logger.isInfoEnabled()) { logger.info("Stopping beans in phase " + this.phase); } Collections.sort(this.members, Collections.reverseOrder()); //這裡有個同步邏輯,CounDownLatch中計數器的數量為當前LifecycleGroup中Lifecycle例項數量 CountDownLatch latch = new CountDownLatch(this.smartMemberCount); Set countDownBeanNames = Collections.synchronizedSet(new LinkedHashSet()); for (LifecycleGroupMember member : this.members) { //這個containsKey判斷很重要,在doStop方法中,SmartLifecycle的stop方法可能會在新執行緒中執行,執行時如果發現了bean的依賴bean,會先去執行依賴bean的stop方法, //因此有可能此處的Lifecycle例項是例項A的依賴bean,已經在執行A例項的stop時執行過stop方法了,執行stop方法完成的時候會將自己從this.lifecycleBeans中remove掉,所以在this.lifecycleBeans就不存在了 if (this.lifecycleBeans.containsKey(member.name)) { doStop(this.lifecycleBeans, member.name, latch, countDownBeanNames); } else if (member.bean instanceof SmartLifecycle) { latch.countDown(); } } try { //等到所有Lifecycle例項都執行完畢,當前執行緒才會執行下去 latch.await(this.timeout, TimeUnit.MILLISECONDS); if (latch.getCount() > 0 && !countDownBeanNames.isEmpty() && logger.isWarnEnabled()) { logger.warn(“Failed to shut down " + countDownBeanNames.size() + " bean” + (countDownBeanNames.size() > 1 ? “s” : “”) + " with phase value " + this.phase + " within timeout of " + this.timeout + ": " + countDownBeanNames); } } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } }

以上程式碼有一處需要注意: SmartLifecycle例項有個stop(Runnable)方法,實現的時候可以在另一個執行緒中執行stop的邏輯,這樣就可以多個SmartLifecycle例項並行執行stop邏輯了,可以提高執行速度,當前執行緒為了等待所有執行stop的執行緒,用了CountDownLatch來等待,為了避免無限期等待還設定了超時時間;

  1. 最後來看看LifecycleGroup的stop方法中迴圈呼叫的doStop方法吧,這裡面才會真正的呼叫到Lifecycle例項的stop方法,還有上面我們分析的多執行緒邏輯:

private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, final String beanName, final CountDownLatch latch, final Set countDownBeanNames) { //從成員變數lifecycleBeans中remove當前bean,表示已經執行過stop方法 Lifecycle bean = lifecycleBeans.remove(beanName); if (bean != null) { //找出依賴bean,通過迭代呼叫來保證依賴bean先執行stop方法 String[] dependentBeans = this.beanFactory.getDependentBeans(beanName); for (String dependentBean : dependentBeans) { //迭代 doStop(lifecycleBeans, dependentBean, latch, countDownBeanNames); } try { //isRunning方法返回true才會執行stop,因此自定義Lifecycle的時候要注意 if (bean.isRunning()) { if (bean instanceof SmartLifecycle) { if (logger.isDebugEnabled()) { logger.debug(“Asking bean '” + beanName + “’ of type [” + bean.getClass() + “] to stop”); } countDownBeanNames.add(beanName); //傳入CountDownLatch減一的邏輯,這樣SmartLifecycle的stop方法中就可以使用新執行緒來執行相關邏輯了,記得執行完畢後再執行Runnable中的邏輯,這樣主執行緒才不會一直等待; ((SmartLifecycle) bean).stop(new Runnable() { @Override public void run() { latch.countDown(); countDownBeanNames.remove(beanName); if (logger.isDebugEnabled()) { logger.debug(“Bean '” + beanName + “’ completed its stop procedure”); } } }); } else { if (logger.isDebugEnabled()) { logger.debug(“Stopping bean '” + beanName + “’ of type [” + bean.getClass() + “]”); } //如果不是SmartLifecycle例項,就呼叫stop,在當前執行緒中執行 bean.stop(); if (logger.isDebugEnabled()) { logger.debug(“Successfully stopped bean '” + beanName + “’”); } } } else if (bean instanceof SmartLifecycle) { // CountDownLatch中計數器的數量是按照SmartLifecycle例項的數量來算的,如果不在runing狀態,例項的stop方法就不會呼叫,主執行緒就不用等待這次stop,latch直接減一 latch.countDown(); } } catch (Throwable ex) { if (logger.isWarnEnabled()) { logger.warn(“Failed to stop bean '” + beanName + “’”, ex); } } } }

從以上程式碼可以看出,SmartLifecycle實現類的stop(Runnable)被呼叫時,LifecycleGroup已經將stop呼叫完畢後要做的工作通過Runnable傳遞給實現類了,因此實現類中要記得執行Runnable的run方法,否則會導致外部呼叫邏輯的引數不準備,影響呼叫執行緒的執行;

以上就是關閉容器階段對SmartLifecycle例項的處理邏輯,簡單的小結如下:

  1. AbstractApplicationContext的doClose方法在容器關閉時會被執行,此處呼叫LifecycleProcessor的onClose方法,由LifecycleProcessor負責所有Lifecycle例項的關閉操作;
  2. 將所有的Lifecycle例項按照phase分組;
  3. 從phase值最大的分組開始,依次執行其中每個Lifecycle物件的stop方法;
  4. 對每個SmartLifecycle例項,若想並行執行以加快stop執行速度,可以在stop方法中用新的執行緒來執行stop業務邏輯,但是最後不要忘記呼叫Runnable入參的run方法,以完成主執行緒的計數和統計;
  5. 主執行緒使用了CountDownLatch,在呼叫了SmartLifecycle例項的stop方法後就會等待,等到計數達到SmartLifecycle總數或者等待超時,再繼續向後執行;

關於容器啟動時的Lifecycle的處理就分析到這裡,接下來看看容器關閉時對Lifecycle操作; Lifecycle和SmartLifecycle,自定義的時候用哪個?

看了上面的原始碼分析,我們對Lifecycle和SmartLifecycle有了更全面的認知,如果對執行順序沒有要求,在關閉的時候也沒有效能或者時間要求,那麼就用Lifecycle吧,因為更簡單,如果在乎順序,也期望關閉時多個Lifecycle例項能並行執行,快速結束,SmartLifecycle無疑更適合;

理論上已經基本熟悉了,接下來通過一次實戰來加深印象,我們自定義一個SmartLifecycle的實現類,並在springboot中驗證以下; 實戰SmartLifecycle介面擴充套件

本次實戰的內容是建立一個springboot工程,在裡面自定義一個SmartLifecycle介面的實現類,如果您不想敲程式碼,也可以去github下載原始碼,地址和連結資訊如下表所示: 名稱 連結 備註 專案主頁 https://github.com/zq2599/blog_demos 該專案在GitHub上的主頁 git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該專案原始碼的倉庫地址,https協議 git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該專案原始碼的倉庫地址,ssh協議

這個git專案中有多個資料夾,本章原始碼在資料夾customizelifecycle下,如下圖紅框所示: 這裡寫圖片描述

接下來開始實戰吧:

  1. 基於maven建立一個springboot的web工程,名為customizelifecycle,pom.xml如下:
<?xml version="1.0" encoding="UTF-8"?>

4.0.0

<groupId>com.bolingcavalry</groupId>
<artifactId>customizelifecycle</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>customizelifecycle</name>
<description>Demo project for Spring Boot</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.15.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  1. 建立Utils.java,裡面提供常用的靜態方法,本次會用到的是printTrack方法,用來列印當前堆疊,便於我們觀察程式執行情況:

package com.bolingcavalry.customizelifecycle.util;

import org.slf4j.Logger; import org.slf4j.LoggerFactory;

/**

  • @Description : 提供一些常用的工具方法

  • @Date : 2018-08-14 05:51 */ public class Utils {

    private static final Logger logger = LoggerFactory.getLogger(Utils.class);

    /**

    • 列印當前執行緒堆疊資訊

    • @param prefix */ public static void printTrack(String prefix){ StackTraceElement[] st = Thread.currentThread().getStackTrace();

      if(null==st){ logger.info(“invalid stack”); return; }

      StringBuffer sbf =new StringBuffer();

      for(StackTraceElement e:st){ if(sbf.length()>0){ sbf.append(" <- "); sbf.append(System.getProperty(“line.separator”)); }

       sbf.append(java.text.MessageFormat.format("{0}.{1}() {2}"
               ,e.getClassName()
               ,e.getMethodName()
               ,e.getLineNumber()));
      

      }

      logger.info(prefix + “\n************************************************************\n” + sbf.toString() + “\n************************************************************”); } }

  1. 建立自定義SmartLifecycle實現類CustomizeLifeCycleLinstener.java,主要程式碼都有註釋說明,就不多贅述了,前面咱們分析的幾個呼叫方法都有日誌列印,便於在執行的時候觀察,另外需要注意的是stop(Runnable)方法的實現中用了一個新的執行緒來執行關閉的邏輯,並且入參Runnable的run方法一定要呼叫:

package com.bolingcavalry.customizelifecycle.lifecycle;

import com.bolingcavalry.customizelifecycle.util.Utils; import org.springframework.context.SmartLifecycle; import org.springframework.stereotype.Component;

/**

  • @Description : SmartLifecycle的實現類,在spring容器初始化完畢和關閉的時候被spring容器回撥,完成特定的業務需求

  • @Date : 2018-08-25 13:59 */ @Component public class CustomizeLifeCycleLinstener implements SmartLifecycle {

    public boolean isRunningFlag() { return runningFlag; }

    public void setRunningFlag(boolean runningFlag) { this.runningFlag = runningFlag; }

    private boolean runningFlag = false;

    @Override public void stop(Runnable callback) {

     new Thread(new Runnable() {
         @Override
         public void run() {
             Utils.printTrack("do stop with callback param");
             //設定為false,表示已經不在執行中了
             setRunningFlag(false);
             //callback中有個CountDownLatch例項,總數是SmartLifecycle物件的數量,
             //此方法被回撥時CountDownLatch例項才會減一,初始化容器的執行緒一直在wait中;
             callback.run();
         }
     }).start();
    

    }

    @Override public void start() { Utils.printTrack(“do start”); //設定為false,表示正在執行中 setRunningFlag(true); }

    @Override public void stop() { Utils.printTrack(“do stop”); //設定為false,表示已經不在執行中了 setRunningFlag(false); }

    @Override public int getPhase() { return 666; }

    @Override public boolean isRunning() { return isRunningFlag(); }

    @Override public boolean isAutoStartup() { //只有設定為true,start方法才會被回撥 return true; } }

  1. 啟動類CustomizelifecycleApplication.java如下:

package com.bolingcavalry.customizelifecycle;

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication public class CustomizelifecycleApplication {

public static void main(String[] args) {
    SpringApplication.run(CustomizelifecycleApplication.class, args);
}

}

  1. 編碼完畢,啟動應用,日誌如下(篇幅所限,前面那段springboot啟動的常見日誌去掉了):

2018-08-26 14:43:11.099 INFO 8008 — [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2018-08-26 14:43:11.102 INFO 8008 — [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 666 2018-08-26 14:43:11.104 INFO 8008 — [ main] c.b.customizelifecycle.util.Utils : do start

java.lang.Thread.getStackTrace() 1,559 <- com.bolingcavalry.customizelifecycle.util.Utils.printTrack() 20 <- com.bolingcavalry.customizelifecycle.lifecycle.CustomizeLifeCycleLinstener.start() 45 <- org.springframework.context.support.DefaultLifecycleProcessor.doStart() 173 <- org.springframework.context.support.DefaultLifecycleProcessor.access200()50&lt;org.springframework.context.support.DefaultLifecycleProcessor200() 50 &lt;- org.springframework.context.support.DefaultLifecycleProcessorLifecycleGroup.start() 346 <- org.springframework.context.support.DefaultLifecycleProcessor.startBeans() 149 <- org.springframework.context.support.DefaultLifecycleProcessor.onRefresh() 112 <- org.springframework.context.support.AbstractApplicationContext.finishRefresh() 880 <- org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh() 144 <- org.springframework.context.support.AbstractApplicationContext.refresh() 546 <- org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh() 122 <- org.springframework.boot.SpringApplication.refresh() 693 <- org.springframework.boot.SpringApplication.refreshContext() 360 <- org.springframework.boot.SpringApplication.run() 303 <- org.springframework.boot.SpringApplication.run() 1,118 <- org.springframework.boot.SpringApplication.run() 1,107 <- com.bolingcavalry.customizelifecycle.CustomizelifecycleApplication.main() 10

2018-08-26 14:43:11.122 INFO 8008 — [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http) 2018-08-26 14:43:11.125 INFO 8008 — [ main] c.b.c.CustomizelifecycleApplication : Started CustomizelifecycleApplication in 1.325 seconds (JVM running for 2.096)

上述日誌可以看到CustomizeLifeCycleLinstener的日誌輸出和執行堆疊,與預期一致;

  1. 接下來驗證關閉的邏輯了,有兩種方式可以驗證,第一種是將當前的應用做成jar包執行,在控制檯輸入”CTRL+C”即可觸發容器關閉,還有一種更簡單的,如果您用的是IDEA開發,那麼請用IDEA將應用啟動,關閉的時候點選下圖紅框中的按鈕,即可觸發容器關閉: 這裡寫圖片描述

  2. 關閉日誌如下所示:

2018-08-26 14:49:47.306 INFO 8008 — [ Thread-6] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot[email protected]27c6e487: startup date [Sun Aug 26 14:43:10 GMT+08:00 2018]; root of context hierarchy 2018-08-26 14:49:47.307 INFO 8008 — [ Thread-6] o.s.c.support.DefaultLifecycleProcessor : Stopping beans in phase 666 2018-08-26 14:49:47.309 INFO 8008 — [ Thread-11] c.b.customizelifecycle.util.Utils : do stop with callback param

java.lang.Thread.getStackTrace() 1,559 <- com.bolingcavalry.customizelifecycle.util.Utils.printTrack() 20 <- com.bolingcavalry.customizelifecycle.lifecycle.CustomizeLifeCycleLinstener$1.run() 32 <- java.lang.Thread.run() 748

2018-08-26 14:49:47.310 INFO 8008 — [ Thread-6] o.s.j.e.a.AnnotationMBeanExporter : Unregistering JMX-exposed beans on shutdown

Process finished with exit code 1

如上述日誌所示,由於CustomizeLifeCycleLinstener的stop方法中新建了一個執行緒來執行操作,因此日誌中的堆疊是這個新執行緒的堆疊資訊,如果您想看到主執行緒的呼叫堆疊,請去掉new Thread的程式碼再次執行即可;

至此,SmartLifecycle介面的原始碼分析和自定義實戰就全部結束了,對spring強大的擴充套件能力又多了一分認識,真心希望本文能助您在感知容器變化的開發中收穫一些啟發,當然,spring中還有更多精彩的擴充套件等著我們去探索,下一篇咱們繼續;