写点什么

spring4.1.8 扩展实战之四:感知 spring 容器变化 (SmartLifecycle 接口)

作者:程序员欣宸
  • 2022 年 6 月 15 日
  • 本文字数:12464 字

    阅读完需:约 41 分钟

spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)

欢迎访问我的 GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本章是《spring4.1.8 扩展实战》的第四篇,如果业务上需要在 spring 容器启动和关闭的时候做一些操作,可以自定义 SmartLifecycle 接口的实现类来扩展,本章我们通过先分析再实战的方法,来掌握这种扩展方式;

往期扩展链接

前面三章已经做了一些扩展,地址如下:


  1. 《spring4.1.8扩展实战之一:自定义环境变量验证》

  2. 《spring4.1.8扩展实战之二:Aware接口揭秘》

  3. 《spring4.1.8扩展实战之三:广播与监听》

本章概要

  • 本章由以下几部分组成:


  1. SmartLifecycle 接口概览;

  2. spring 容器启动与 SmartLifecycle 的关系;

  3. spring 容器关闭与 SmartLifecycle 的关系;

  4. 关于 Lifecycle 和 SmartLifecycle;

  5. 实战 SmartLifecycle 接口扩展;

SmartLifecycle 接口概览

  • 先来看看 SmartLifecycle 接口的类图:


  • 如上图所示,在继承了 Lifecycle 和 Phased 两个接口后,SmartLifecycle 一共定义了六个方法,为了便于后面的源码分析,先做个简介:



  • 从上述列举中可以看出,感知容器变化的能力最终来自 Lifecycle,而 SmartLifecycle 只是 Lifecycle 的增强版,可以自定义优先级(getPhase),自主决定是否随容器启动(isAutoStartup),以及停止时能接受一个 runnable 对象(stop(Runnable));

spring 容器启动与 SmartLifecycle 的关系

  • 现在可以结合 spring 源码来看看 SmartLifecycle 的使用场景,从 spring 容器初始化看起;

  • AbstractApplicationContext 类的 refresh 方法中,在 bean 的实例化和初始化操作完毕后,会调用 finishRefresh 方法,如下图红框所示:



  • 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 开始看起吧;

  • 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 + "]");      }    }  }
复制代码


  • 接下来是==getLifecycleProcessor().onRefresh()==的执行,如果业务不自定义一个 LifecycleProcessor,就默认创建一个 DefaultLifecycleProcessor 对象,因此执行的就是 DefaultLifecycleProcessor 的 onRefresh 方法,来看看源码:


@Overridepublic 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<Integer> keys = new ArrayList<Integer>(phases.keySet());      //按照所有的phase值排序,然后依次执行bean的start方法,每次都是一批phase相同的      Collections.sort(keys);      for (Integer key : keys) {        //这里面会对所有Lifecycle实例逐个调用start方法        phases.get(key).start();      }    }  }
复制代码


  • 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 方法被调用了,去看看该方法的调用处;<br>

  • LifecycleProcessor 的 onClose 方法是在 AbstractApplicationContext 的 doClose 方法中被调用的,如下图红框所示,这是汇集了容器关闭时要执行的基本逻辑:



  • 弄清了调用逻辑,可以去 DefaultLifecycleProcessor 中看看 SmartLifecycle 实例的 stop 方法是如何被调用的;

  • DefaultLifecycleProcessor 的 stop 方法中先调用 stopBeans 方法,再将成员变量 running 设置为 false,表示状态已不是运行中:


@Overridepublic void stop() {  stopBeans();  this.running = false;}
复制代码


  • 展开 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<Integer> keys = new ArrayList<Integer>(phases.keySet());      //按照phase排序,和启动的时候的排序正好相反      Collections.sort(keys, Collections.reverseOrder());      for (Integer key : keys) {        //对phase相同的Lifecycle实例,逐一执行stop方法        phases.get(key).stop();      }    }  }
复制代码


  • 上述代码和启动时执行 start 的逻辑基本相似,不同的是执行顺序正好相反;

  • 看看 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<String> countDownBeanNames = Collections.synchronizedSet(new LinkedHashSet<String>());      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 来等待,为了避免无限期等待还设置了超时时间;

  • 最后来看看 LifecycleGroup 的 stop 方法中循环调用的 doStop 方法吧,这里面才会真正的调用到 Lifecycle 实例的 stop 方法,还有上面我们分析的多线程逻辑:


private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, final String beanName,      final CountDownLatch latch, final Set<String> 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 下载源码,地址和链接信息如下表所示:



  • 这个 git 项目中有多个文件夹,本章源码在文件夹 customizelifecycle 下,如下图红框所示:



  • 接下来开始实战吧:

  • 基于 maven 创建一个 springboot 的 web 工程,名为 customizelifecycle,pom.xml 如下:


<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  <modelVersion>4.0.0</modelVersion>
<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></project>
复制代码


  • 创建 Utils.java,里面提供常用的静态方法,本次会用到的是 printTrack 方法,用来打印当前堆栈,便于我们观察程序执行情况:


package com.bolingcavalry.customizelifecycle.util;
import org.slf4j.Logger;import org.slf4j.LoggerFactory;
/** * @Description : 提供一些常用的工具方法 * @Author : zq2599@gmail.com * @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************************************************************"); }}
复制代码


  • 创建自定义 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容器回调,完成特定的业务需求 * @Author : zq2599@gmail.com * @Date : 2018-08-25 13:59 */@Componentpublic 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; }}
复制代码


  • 启动类 CustomizelifecycleApplication.java 如下:


package com.bolingcavalry.customizelifecycle;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class CustomizelifecycleApplication {
public static void main(String[] args) { SpringApplication.run(CustomizelifecycleApplication.class, args); }}
复制代码


  • 编码完毕,启动应用,日志如下(篇幅所限,前面那段 springboot 启动的常见日志去掉了):


2018-08-26 14:43:11.099  INFO 8008 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup2018-08-26 14:43:11.102  INFO 8008 --- [           main] o.s.c.support.DefaultLifecycleProcessor  : Starting beans in phase 6662018-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.access$200() 50 <-org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.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 的日志输出和执行堆栈,与预期一致;

  • 接下来验证关闭的逻辑了,有两种方式可以验证,第一种是将当前的应用做成 jar 包运行,在控制台输入"CTRL+C"即可触发容器关闭,还有一种更简单的,如果您用的是 IDEA 开发,那么请用 IDEA 将应用启动,关闭的时候点击下图红框中的按钮,即可触发容器关闭:


  • 关闭日志如下所示:


2018-08-26 14:49:47.306  INFO 8008 --- [       Thread-6] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@27c6e487: startup date [Sun Aug 26 14:43:10 GMT+08:00 2018]; root of context hierarchy2018-08-26 14:49:47.307  INFO 8008 --- [       Thread-6] o.s.c.support.DefaultLifecycleProcessor  : Stopping beans in phase 6662018-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 中还有更多精彩的扩展等着我们去探索,下一篇咱们继续;

欢迎关注 InfoQ:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

发布于: 2022 年 06 月 15 日阅读数: 47
用户头像

搜索"程序员欣宸",一起畅游Java宇宙 2018.04.19 加入

前腾讯、前阿里员工,从事Java后台工作,对Docker和Kubernetes充满热爱,所有文章均为作者原创,个人Github:https://github.com/zq2599/blog_demos

评论

发布
暂无评论
spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)_Java_程序员欣宸_InfoQ写作社区