spring4.1.8扩展实战之四:感知spring容器变化(smartlifecycle接口)(代码片段)

程序员欣宸 程序员欣宸     2022-11-28     627

关键词:

欢迎访问我的GitHub

本篇概览

  • 本章是《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一共定义了六个方法,为了便于后面的源码分析,先做个简介:
方法 作用
start() bean初始化完毕后,该方法会被执行
stop() 容器关闭后:<br>spring容器发现当前对象实现了SmartLifecycle,就调用stop(Runnable),<br>如果只是实现了Lifecycle,就调用stop()
isRunning() 当前状态
getPhase() 返回值决定start方法在众多Lifecycle实现类中的执行顺序(stop也是)
isAutoStartup() start方法被执行前先看此方法返回值,返回false就不执行start方法了
stop(Runnable) 容器关闭后:<br>spring容器发现当前对象实现了SmartLifecycle,就调用stop(Runnable),<br>如果只是实现了Lifecycle,就调用stop()
  • 从上述列举中可以看出,感知容器变化的能力最终来自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方法,来看看源码:
@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<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,表示状态已不是运行中:
@Override
public 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下载源码,地址和链接信息如下表所示:
名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个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
 */
@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;
    
  • 启动类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);
    
  • 编码完毕,启动应用,日志如下(篇幅所限,前面那段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.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 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中还有更多精彩的扩展等着我们去探索,下一篇咱们继续;

欢迎关注51CTO博客:程序员欣宸

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

欢迎访问我的GitHub关于扩展在之前学习spring环境初始化源码的过程中,见到有些地方能通过子类来实现自定义扩展,从本章开始,我们来逐个实践这些扩展,除了加深对spring的理解,有的扩展也能解决一些通用的问题;文中涉及... 查看详情

spring4.1.8扩展实战之八:import注解(代码片段)

欢迎访问我的GitHub在spring框架下做开发时,@Import是常见的注解,可以用来动态创建bean,今天我们先从源码分析原理,再用实战来验证Import的作用;文章概览本章由以下几部分组成:从Enable前缀的注解谈起,揭示常见的Enable注解... 查看详情

spring4.1.8扩展实战之五:改变bean的定义(beanfactorypostprocessor接口)(代码片段)

欢迎访问我的GitHub本篇概览本章我们继续实战spring的扩展能力,通过自定义BeanFactoryPostProcessor接口的实现类,来对bean实例做一些控制;BeanFactoryPostProcessor接口简介spring容器初始化时,从资源中读取到bean的相关定义后,保存在bean... 查看详情

spring4.1.8扩展实战之六:注册bean到spring容器(beandefinitionregistrypostprocessor接口)(代码片段)

欢迎访问我的GitHub本章是《spring4.1.8扩展实战》系列的第六篇,目标是学习如何通过自己写代码的方式,向spring容器中注册bean;关于注册bean到容器我们开发的类,如果想注册到spring容器,让spring来完成实例化,常用方式如下:xm... 查看详情

乐字节java8核心特性实战之四:方法引用

student_score表:+------+---------+-------+|name|subject|score|+------+---------+-------+|张三|语文|78||张三|数学|88||张三|英语|98||李四|语文|89||李四|数学|76||李四|英语|90||王五|语文|89||王五|数学|66||王五|英语|91|+------+---------+----- 查看详情

springcloudgateway实战之四:内置predicate小结(代码片段)

...ps://github.com/zq2599/blog_demos本篇概览本文是《SpringCloudGateway实战》系列的第四篇,咱们将已有的断言(predicate)的类型做个小结,今天的内容中,除了官方推荐的简化版配置ÿ 查看详情

springcloudgateway实战之四:内置predicate小结(代码片段)

...ps://github.com/zq2599/blog_demos本篇概览本文是《SpringCloudGateway实战》系列的第四篇,咱们将已有的断言(predicate)的类型做个小结,今天的内容中,除了官方推荐的简化版配置ÿ 查看详情

bert-多标签文本分类实战之四——数据集预处理(代码片段)

·请参考本系列目录:【BERT-多标签文本分类实战】之一——实战项目总览·下载本实战项目资源:>=点击此处=<[1]数据集预处理的流程  在拿到数据集之后,我们关心接下来操作的步骤:  ·查看数... 查看详情

bert-多标签文本分类实战之四——数据集预处理(代码片段)

·请参考本系列目录:【BERT-多标签文本分类实战】之一——实战项目总览·下载本实战项目资源:>=点击此处=<[1]数据集预处理的流程  在拿到数据集之后,我们关心接下来操作的步骤:  ·查看数... 查看详情

bert-多标签文本分类实战之四——数据集预处理(代码片段)

·请参考本系列目录:【BERT-多标签文本分类实战】之一——实战项目总览·下载本实战项目资源:>=点击此处=<[1]数据集预处理的流程  在拿到数据集之后,我们关心接下来操作的步骤:  ·查看数... 查看详情

自动驾驶感知算法实战——感知系统整体概述

自动驾驶感知算法实战专栏:https://blog.csdn.net/charmve/category_12097938.html零、感知系统整体概述(5%)(框图)在自动驾驶系统中的位置,上下游解决什么问题实现方案 查看详情

spring4.1.8初始化源码学习三部曲之三:abstractapplicationcontext.refresh方法

欢迎访问我的GitHub我们先回顾ClassPathXmlApplicationContext类的初始化过程如下代码:publicClassPathXmlApplicationContext(String[]configLocations,booleanrefresh,ApplicationContextparent)throwsBeansExceptionsuper(parent);setCon 查看详情

自动驾驶感知算法实战——感知系统整体概述

自动驾驶感知算法实战专栏:https://blog.csdn.net/charmve/category_12097938.html零、感知系统整体概述(5%)(框图)在自动驾驶系统中的位置,上下游解决什么问题实现方案当前多数自动驾驶系统框架举例:自... 查看详情

自动驾驶感知算法实战14——感知算法模型生产线

自动驾驶感知算法实战专栏:https://blog.csdn.net/charmve/category_12097938.html目录一、感知算法生产流程二、算法模型部署流程二、各个阶段交付物数据选择(数据采集、数据增强)数据标注模型训练模型量化模型部署测试与验证一、... 查看详情

java扩展nginx之四:远程调试

...):https://github.com/zq2599/blog_demos本篇概览本文是《Java扩展Nginx》系列的第四篇,一起来体验个实用的功能,此能力定会让爱学习的您大呼过瘾,它就是广大java程序员在日常开发和定位问题过程中常用的神技:远... 查看详情

自动驾驶感知算法实战3——自动驾驶2d和3d视觉感知算法概述

自动驾驶感知算法实战专栏:https://blog.csdn.net/charmve/category_12097938.html目录导读一、2D视觉感知1.目标检测1.1两阶段检测1.2单阶段检测1.3Anchor-free检测(无Anchor检测)1.4Transformer检测2.目标跟踪3.语义分割二、3D视觉感知1.单目3D感知... 查看详情

自动驾驶感知算法实战3——自动驾驶2d和3d视觉感知算法概述

自动驾驶感知算法实战专栏:https://blog.csdn.net/charmve/category_12097938.html目录导读一、2D视觉感知1.目标检测1.1两阶段检测1.2单阶段检测1.3Anchor-free检测(无Anchor检测)1.4Transformer检测2.目标跟踪3.语义分割二、3D视觉感知1.单目3D感知... 查看详情

vmware后台下citrixxendesktop7.6实战篇之四sql数据库安装部署

1、        2008r2安装调整1.1系統要求站点配置数据库(最初包括配置日志记录数据库和监视数据库)支持的MicrosoftSQLServer版本:SQLServer2014Express  Edition、Standard  Edition和EnterpriseEdi 查看详情