在阅读Kafka
源码时,读到KafkaMessageListenerContainer
类,在它的入口方法 doStart()
中,第一个逻辑区就是根据isRunning()
方法的返回值来判断方法是否继续运行下去:
KafkaMessageListenerContainer#doStart:
protected void doStart() {
if (isRunning()) {
return;
}
if (this.clientIdSuffix == null) { // stand-alone container
checkTopics();
}
ContainerProperties containerProperties = getContainerProperties();
checkAckMode(containerProperties);
......
}
那isRunning()
方法是什么呢,往上追溯发现它是SmartLifecycle
接口中定义的方法,所以KafkaMessageListenerContainer
类实现了一个名为SmartLifecycle
接口。如果你看过多数框架的源码,就会发现SmartLifecycle
接口的出现率很高,它是什么,有什么用呢?就让我们一起来深入了解它吧!
在讲解SmartLifecycle
接口时,先讲解SmartLifecycle
接口的父接口Lifecycle
,不用担心,它们的作用是一样,不过SmartLifecycle
作为子类功能会更强大点,但Lifecycle
接口还是有必要了解的。
Lifecycle
接口它允许开发者在所有Bean创建完成之后执行自定义的一些任务并运行,并在退出时执行资源销毁流程。
@PostConstruct
和@PreDestroy
两个注解,它们是在Bean初始化或销毁时执行一些操作,这些操作属于Bean生命周期级别;Lifecycle
接口解决的是另外一些场景,比如我们想在容器本身的生命周期(比如容器启动、停止)的事件上做一些工作。我们先来看看它的定义:
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
Lifecycle
接口定义了三个方法:
方法名 | 作者 |
---|---|
start() | 容器启动后调用 |
stop() | 容器停止时调用 |
isRunning() | 检查此组件是否正在运行 |
当ApplicationContext
接收到start
、stop
和restart
等信号时,会调用实现了Lifecycle
接口的Bean的相应方法。通过实现Lifecycle
接口,我们可以获得对容器生命周期的回调,从而实现业务扩展。
我们来实现Lifecycle
接口,来看看它具体的实践效果:
@Component
public class MyLifecycle implements Lifecycle {
/**
* 运行状态
*/
private volatile boolean running = false;
/**
* 容器启动后调用
*/
@Override
public void start() {
System.out.println("收到容器启动的信号后,执行MyLifecycle的start操作...");
running = true;
}
/**
* 容器停止时调用
*/
@Override
public void stop() {
System.out.println("收到关闭容器的信号后,执行MyLifecycle的stop操作...");
running = false;
}
/**
* 检查此组件是否正在运行。
* 1. 只有该方法返回false时,start()方法才会被执行,容器启动后。
* 2. 只有该方法返回true时,stop()方法才会被执行,容器停止时。
*/
@Override
public boolean isRunning() {
System.out.println("检查MyLifecycle组件的运行状态:" + running);
return running;
}
}
然后来启动SpringBoot项目,会发现启动时并没有打印出任何相关的日志,只有在关闭应用时会打印出:
收到关闭容器的信号后,执行MyLifecycle的stop操作...
为什么呢?在SpringBoot
或Spring
应用中,如果只是实现了Lifecycle接口而没有显式调用AbstractApplicationContext的start()方法,那么Lifecycle接口中的start()方法和isRunning()方法不会被执行。然而,在应用退出时,会执行isRunning()
方法来判断该Lifecycle
是否已经启动,如果返回true,则会调用stop()
方法来停止。
这种实现方式需要使用者显式地调用容器的start()
和stop()
方法才能触发Lifecycle
接口的方法执行,而在一般的项目中,我们很少这样显式的去调用。为了解决这个问题,Spring
提供了SmartLifecycle
接口,下面就开始讲解它。
SmartLifecycle
继承了Lifecycle
接口,并且提供了更智能的功能:
SmartLifecycle
接口的组件可以自动启动和停止;在应用启动时,会自动调用实现了SmartLifecycle
接口的组件的start()
方法,而无需显式调用容器的start()
方法。因此,如果希望组件的生命周期方法能够自动执行而无需显式调用容器的方法,可以考虑实现SmartLifecycle
接口而不是仅仅实现Lifecycle
接口。
先来看一下SmartLifecycle接口的源码:
public interface SmartLifecycle extends Lifecycle, Phased {
int DEFAULT_PHASE = Integer.MAX_VALUE;
default boolean isAutoStartup() {
return true;
}
default void stop(Runnable callback) {
stop();
callback.run();
}
@Override
default int getPhase() {
return DEFAULT_PHASE;
}
}
可以看出,SmartLifecycle
接口除了继承Lifecycle
接口外,还通过继承Phased
接口来获得控制执行顺序的能力。其中,getPhase()
方法来自Phased
接口,通过返回阶段值来确定SmartLifecycle
组件的执行顺序。因此,通过实现SmartLifecycle
和Phased
接口,可以灵活地控制组件的生命周期和执行顺序。
我们来实现SmartLifecycle
接口,来看看它具体的实践效果:
@Component
public class MySmartLifecycle implements SmartLifecycle {
private volatile boolean running = false;
/**
* 如果该Lifecycle类所在的上下文在调用时,希望能够自己自动进行回调,则返回true,
* false的值表明组件打算通过显式的start()调用来启动,类似于普通的Lifecycle实现。
*/
@Override
public boolean isAutoStartup() {
return true;
}
/**
* SmartLifecycle子类的才有的方法,当isRunning方法返回true时,该方法才会被调用。
*/
@Override
public void stop(Runnable callback) {
System.out.println("MySmartLifecycle容器停止 stop(Runnable)方法 有回调函数...");
// 调用无回调函数的stop方法
stop();
// 如果你让isRunning返回true,就侍执行stop这个方法,那么就不要忘记调用callback.run()。
// 否则在程序退出时,Spring的DefaultLifecycleProcessor会认为这个MySmartLifecycle没有stop完成,程序会一直卡着结束不了,等待一定时间(默认超时时间30秒)后才会自动结束。
callback.run();
}
/**
* 1. 主要在该方法中 启动一些任务 或者 运行一些其他异步服务,比如开启MQ接收消息
* 2. 当上下文被刷新(所有对象已被实例化和初始化之后)时,将调用该方法,
* 默认生命周期处理器将检查每个SmartLifecycle对象的isAutoStartup()方法返回的布尔值。
* 如果为“true”,则该方法会被调用,而不是等待显式调用自己的start()方法。
*/
@Override
public void start() {
System.out.println("MySmartLifecycle容器启动完成 start()方法...");
running = true;
}
/**
* 接口Lifecycle子类的方法,只有非SmartLifecycle的子类才会执行该方法。
* 1. 该方法只对直接实现接口Lifecycle的类才起作用,对实现SmartLifecycle接口的类无效。
* 2. 方法stop()和方法stop(Runnable callback)的区别只在于,后者是SmartLifecycle子类的专属。
*/
@Override
public void stop() {
System.out.println("MySmartLifecycle容器停止 stop()方法 无回调函数...");
running = false;
}
/**
* 1. 只有该方法返回false时,start方法才会被执行。
* 2. 只有该方法返回true时,stop(Runnable callback)方法才会被执行。
*/
@Override
public boolean isRunning() {
System.out.println("MySmartLifecycle检查运行状态 isRunning()方法...");
return running;
}
/**
* 如果有多个实现接口SmartLifecycle的类,
* 则这些类的start()方法的执行顺序按getPhase()方法返回值从小到大执行,例如:1比2先执行,-1比0先执行。
* 而这些类的stop()方法的执行顺序按getPhase()方法返回值从大到小执行,例如:2先执行,1后执行,0最后执行。
*/
@Override
public int getPhase() {
return 0;
}
}
关于每个方法的功能,注释部分已经明确说明了,下面启动SpringBoot项目,打印日志如下:
MySmartLifecycle检查运行状态 isRunning()方法... MySmartLifecycle容器启动完成 start()方法...
关闭SpringBoot项目,打印日志如下:
MySmartLifecycle检查运行状态 isRunning()方法... MySmartLifecycle容器停止 stop(Runnable)方法 有回调函数... MySmartLifecycle容器停止 stop()方法 无回调函数...
根据上述示例,可以总结如下:
SmartLifecycle
接口,当Spring
容器启动时会自动执行它的启动方法。在执行启动方法之前,会通过isRunning()
方法检查组件的运行状态,如果返回值为false
表示尚未执行启动操作,此时会调用start()
方法进行启动。Spring
容器关闭时,会检查组件的运行状态,并根据情况执行关闭操作。如果组件正在运行,则会调用相应的停止方法。同时,可以处理相应的回调函数。SmartLifecycle
接口中的getPhase()
方法返回的值越小,优先级越高,表示在启动和停止过程中先执行。文中我们通过先了解 什么是Lifecycle
和 Lifecycle
的作用,根据它所存在的不足,引出比它更强的子类SmartLifecycle
。我们通过实现SmartLifecycle
接口,可以更加灵活地控制组件的启动和停止,并定义它们的执行顺序。SmartLifecycle
的使用有大量的实践案例,所以无论实战或阅读源码,都最好需要了解SmartLifecycle
相关接口。
如果您对本文有任何疑问或需要帮助,请在评论区留言,我会尽力解答。如果本文对您有帮助,请给个赞以示支持,非常感谢!💗
我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。