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

原文链接:https://blog.csdn.net/boling_cavalry/article/details/82051356

往期扩展链接
前面三章已经做了一些扩展,地址如下:
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() 容器关闭后:
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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
上述代码中,initLifecycleProcessor()和getLifecycleProcessor().onRefresh()这两个方法和本章的主题有关,其他两个就不在本章展开了,我们从initLifecycleProcessor开始看起吧;

3. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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

@Override
public void onRefresh() {
startBeans(true);
this.running = true;
}
1
2
3
4
5
展开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();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

5. 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 + "'");
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

以上就是初始化阶段容器对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();

}
1
2
3
4
5
6
7
8
9
10
11
12
13
如上所示,感知容器关闭只能靠onClose方法被调用了,去看看该方法的调用处;
2. LifecycleProcessor的onClose方法是在AbstractApplicationContext的doClose方法中被调用的,如下图红框所示,这是汇集了容器关闭时要执行的基本逻辑:

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

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

@Override
public void stop() {
stopBeans();
this.running = false;
}
1
2
3
4
5

4. 展开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();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
上述代码和启动时执行start的逻辑基本相似,不同的是执行顺序正好相反;

5. 看看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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
以上代码有一处需要注意:
SmartLifecycle实例有个stop(Runnable)方法,实现的时候可以在另一个线程中执行stop的逻辑,这样就可以多个SmartLifecycle实例并行执行stop逻辑了,可以提高执行速度,当前线程为了等待所有执行stop的线程,用了CountDownLatch来等待,为了避免无限期等待还设置了超时时间;

6. 最后来看看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);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
从以上代码可以看出,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下,如下图红框所示:

接下来开始实战吧:
1. 基于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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

2. 创建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************************************************************");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

3. 创建自定义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;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

4. 启动类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
2
3
4
5
6
7
8
9
10
11
12

5. 编码完毕,启动应用,日志如下(篇幅所限,前面那段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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
上述日志可以看到CustomizeLifeCycleLinstener的日志输出和执行堆栈,与预期一致;

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

7. 关闭日志如下所示:

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
1
2
3
4
5
6
7
8
9
10
11
12
如上述日志所示,由于CustomizeLifeCycleLinstener的stop方法中新建了一个线程来执行操作,因此日志中的堆栈是这个新线程的堆栈信息,如果您想看到主线程的调用堆栈,请去掉new Thread的代码再次运行即可;

至此,SmartLifecycle接口的源码分析和自定义实战就全部结束了,对spring强大的扩展能力又多了一分认识,真心希望本文能助您在感知容器变化的开发中收获一些启发,当然,spring中还有更多精彩的扩展等着我们去探索,下一篇咱们继续;
---------------------
作者:博陵精骑
来源:CSDN
原文:https://blog.csdn.net/boling_cavalry/article/details/82051356
版权声明:本文为博主原创文章,转载请附上博文链接!

spring4.1.8扩展实战之四:感知spring容器变化(SmartLifecycle接口)的更多相关文章

  1. spring4.1.8扩展实战之六:注册bean到spring容器(BeanDefinitionRegistryPostProcessor接口)

    本章是<spring4.1.8扩展实战>系列的第六篇,目标是学习如何通过自己写代码的方式,向spring容器中注册bean: 原文地址:https://blog.csdn.net/boli ...

  2. spring4.1.8扩展实战之三:广播与监听

    提到广播与监听,我们常常会想到RabbitMQ.Kafka等消息中间件,这些常用于分布式系统中多个应用之间,有时候应用自身内部也有广播和监听的需求(例如某个核心数据发生变化后,有些业务模块希望立即被感 ...

  3. spring4.1.8扩展实战之八:Import注解

    spring4.1.8扩展实战之八:Import注解 2018年09月10日 12:53:57 博陵精骑 阅读数:441更多 所属专栏: spring4源码分析与实战    版权声明:欢迎转载,请注明 ...

  4. spring4.1.8扩展实战之七:控制bean(BeanPostProcessor接口)

    本章是<spring4.1.8扩展实战>的第七篇,我们来尝试在容器初始化的时候对bean实例做设置: 原文地址:https://blog.csdn.net/boling_cavalry/a ...

  5. spring扩展点之四:Spring Aware容器感知技术,BeanNameAware和BeanFactoryAware接口,springboot中的EnvironmentAware

    aware:英 [əˈweə(r)] 美 [əˈwer] adj.意识到的;知道的;觉察到的 XXXAware在spring里表示对XXX感知,实现XXXAware接口,并通过实现对应的set-XXX ...

  6. spring4.1.8扩展实战之五:改变bean的定义(BeanFactoryPostProcessor接口)

    本章我们继续实战spring的扩展能力,通过自定义BeanFactoryPostProcessor接口的实现类,来对bean实例做一些控制: 原文地址:https://blog.csdn.net/bo ...

  7. spring4.1.8扩展实战之二:Aware接口揭秘

    Aware.java是个没有定义任何方法的接口,拥有众多子接口,在spring源码中有多处都在使用这些子接口完成各种场景下的回调操作,当业务有需要时,我们只需创建类来实现相关接口,再声明为bean,就 ...

  8. spring容器注入一个接口的两个实现类

    spring容器中能拥有两个同种类型的bean吗?我有两个dao类同时实现一个接口,这两个接口注入时报了异常如下. org.springframework.beans.factory.NoSuchBe ...

  9. Spring容器变化之SmartLifecycle,LifecycleProcesso接口详述

    Spring Boot run方法启动后相应的服务也随之启动,这个操作很妙.使用者都不用关心什么服务怎么启动,不管多少个服务怎么启动只要符合Spring Boot的启动规则都可以使用其run方法同一启 ...

随机推荐

  1. mysql压缩包安装相关过程命令

    mysqld --remove mysql57 HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Eventlog\Application\MySQL目 ...

  2. spring-第十五篇之AOP面向切面编程之AspectJ框架简单应用

    1.去官方网站下载aspectj-1.8.0.jar 2.在jar包目录启动cmd,执行java -jar aspectj-1.8.0.jar,Next 3.检查JAVA_HOME路径是否正确,如果不 ...

  3. SUST_ACM_2019届暑期ACM集训热身赛题解

    问题A:Hello SUST! 知识点:基本输入输出 C/C++: #include <stdio.h> int main() { int n; scanf("%d", ...

  4. 3.Golang的包导入

    1.golang的源码文件可以随意命名,但是属于同一个包的源文件必须声明 package base 2.golang的包引入规则 import ( "fmt" #系统包直接写名字 ...

  5. 接口测试工具——postman

    Postman 之前是作为Chrome 的一个插件,现在要下载应用才能使用. 以下是postman 的界面: 各个功能区的使用如下: 快捷区: 快捷区提供常用的操作入口,包括运行收藏夹的一组测试数据, ...

  6. SCUT - 365 - 鹏哥的数字集合 - wqs二分 - 斜率优化dp

    https://scut.online/p/365 https://www.luogu.org/problemnew/solution/P2365 写这篇的时候还不是很明白,看一下这个东西. http ...

  7. VS2015配置OpenCV

    第一步:下载对应版本的VS2015和OpenCV3.4.1---->链接: https://pan.baidu.com/s/1YL_TlLi3k0SehsDY2DJ8nw 提 取码: 6g27 ...

  8. Gradle打包问题Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0

    前言 使用gradle打包react native的时候,出现了如下报错,下面和大家说一下解决的具体办法 Deprecated Gradle features were used in this bu ...

  9. 省流量 转:http://www.wtoutiao.com/p/T3b8it.html

    前言:“客户端上传时间戳”的玩法,你玩过么?一起聊聊时间戳的奇技淫巧! 缘起:无线时代,流量敏感.APP在登录后,往往要向服务器同步非常多的数据,很费流量,技术上有没有节省流量的方法呢?这是本文要讨论 ...

  10. ajaxSubmit 实现图片上传 SSM maven

    文件上传依赖: <!-- 文件上传组件 --> <dependency> <groupId>commons-fileupload</groupId> & ...