前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >玩转Spring生命周期之Lifecycle和SmartLifecycle

玩转Spring生命周期之Lifecycle和SmartLifecycle

原创
作者头像
Blue_007
修改2024-01-16 11:51:12
4996
修改2024-01-16 11:51:12
举报
文章被收录于专栏:代码生涯代码生涯

🍕 一、引言

在阅读Kafka源码时,读到KafkaMessageListenerContainer类,在它的入口方法 doStart() 中,第一个逻辑区就是根据isRunning()方法的返回值来判断方法是否继续运行下去:

KafkaMessageListenerContainer#doStart:

代码语言:Java
复制
protected void doStart() {
 if (isRunning()) {
  return;
 }
 if (this.clientIdSuffix == null) { // stand-alone container
  checkTopics();
 }
 ContainerProperties containerProperties = getContainerProperties();
 checkAckMode(containerProperties);
 ......
}

isRunning()方法是什么呢,往上追溯发现它是SmartLifecycle接口中定义的方法,所以KafkaMessageListenerContainer类实现了一个名为SmartLifecycle接口。如果你看过多数框架的源码,就会发现SmartLifecycle接口的出现率很高,它是什么,有什么用呢?就让我们一起来深入了解它吧!

🍔 二、Lifecycle

在讲解SmartLifecycle接口时,先讲解SmartLifecycle接口的父接口Lifecycle,不用担心,它们的作用是一样,不过SmartLifecycle作为子类功能会更强大点,但Lifecycle接口还是有必要了解的。

🍟 2.1 Lifecycle的作用

Lifecycle接口它允许开发者在所有Bean创建完成之后执行自定义的一些任务并运行,并在退出时执行资源销毁流程。

  1. 这里是所有Bean创建完成之后执行,并不像我们常会用到的@PostConstruct@PreDestroy两个注解,它们是在Bean初始化或销毁时执行一些操作,这些操作属于Bean生命周期级别;
  2. Lifecycle接口解决的是另外一些场景,比如我们想在容器本身的生命周期(比如容器启动、停止)的事件上做一些工作。

🌭 2.2 Lifecycle的定义

我们先来看看它的定义:

代码语言:Java
复制
public interface Lifecycle {
    void start();

    void stop();

    boolean isRunning();
}

Lifecycle接口定义了三个方法:

方法名

作者

start()

容器启动后调用

stop()

容器停止时调用

isRunning()

检查此组件是否正在运行

ApplicationContext接收到startstoprestart等信号时,会调用实现了Lifecycle接口的Bean的相应方法。通过实现Lifecycle接口,我们可以获得对容器生命周期的回调,从而实现业务扩展。

🍿 2.3 Lifecycle的使用

我们来实现Lifecycle接口,来看看它具体的实践效果:

代码语言:java
复制
@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操作...

🥓 2.4 Lifecycle的问题

为什么呢?在SpringBootSpring应用中,如果只是实现了Lifecycle接口而没有显式调用AbstractApplicationContext的start()方法,那么Lifecycle接口中的start()方法和isRunning()方法不会被执行。然而,在应用退出时,会执行isRunning()方法来判断该Lifecycle是否已经启动,如果返回true,则会调用stop()方法来停止。

这种实现方式需要使用者显式地调用容器的start()stop()方法才能触发Lifecycle接口的方法执行,而在一般的项目中,我们很少这样显式的去调用。为了解决这个问题,Spring提供了SmartLifecycle接口,下面就开始讲解它。

🍳 三、SmartLifecycle

🥙 3.1 SmartLifecycle的作用

SmartLifecycle继承了Lifecycle接口,并且提供了更智能的功能:

  1. 实现SmartLifecycle接口的组件可以自动启动和停止;
  2. 可以控制多个组件的执行顺序。

在应用启动时,会自动调用实现了SmartLifecycle接口的组件的start()方法,而无需显式调用容器的start()方法。因此,如果希望组件的生命周期方法能够自动执行而无需显式调用容器的方法,可以考虑实现SmartLifecycle接口而不是仅仅实现Lifecycle接口。

🍖 3.2 SmartLifecycle的定义

先来看一下SmartLifecycle接口的源码:

代码语言:java
复制
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组件的执行顺序。因此,通过实现SmartLifecyclePhased接口,可以灵活地控制组件的生命周期和执行顺序。

🍚 3.3 SmartLifecycle的使用

我们来实现SmartLifecycle接口,来看看它具体的实践效果:

代码语言:java
复制
@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()方法 无回调函数...

🍬 3.4 SmartLifecycle的总结

根据上述示例,可以总结如下:

  1. 如果一个Bean实现了SmartLifecycle接口,当Spring容器启动时会自动执行它的启动方法。在执行启动方法之前,会通过isRunning()方法检查组件的运行状态,如果返回值为false表示尚未执行启动操作,此时会调用start()方法进行启动。
  2. Spring容器关闭时,会检查组件的运行状态,并根据情况执行关闭操作。如果组件正在运行,则会调用相应的停止方法。同时,可以处理相应的回调函数。
  3. SmartLifecycle接口中的getPhase()方法返回的值越小,优先级越高,表示在启动和停止过程中先执行。

🍺 四、小结

文中我们通过先了解 什么是LifecycleLifecycle的作用,根据它所存在的不足,引出比它更强的子类SmartLifecycle。我们通过实现SmartLifecycle接口,可以更加灵活地控制组件的启动和停止,并定义它们的执行顺序。SmartLifecycle的使用有大量的实践案例,所以无论实战或阅读源码,都最好需要了解SmartLifecycle相关接口。

如果您对本文有任何疑问或需要帮助,请在评论区留言,我会尽力解答。如果本文对您有帮助,请给个赞以示支持,非常感谢!💗

我正在参与2024腾讯技术创作特训营第五期有奖征文,快来和我瓜分大奖!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 🍕 一、引言
  • 🍔 二、Lifecycle
    • 🍟 2.1 Lifecycle的作用
      • 🌭 2.2 Lifecycle的定义
        • 🍿 2.3 Lifecycle的使用
          • 🥓 2.4 Lifecycle的问题
          • 🍳 三、SmartLifecycle
            • 🥙 3.1 SmartLifecycle的作用
              • 🍖 3.2 SmartLifecycle的定义
                • 🍚 3.3 SmartLifecycle的使用
                  • 🍬 3.4 SmartLifecycle的总结
                  • 🍺 四、小结
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档