当前位置: 首页 > 科技观察

Spring的Lifecycle和SmartLifecycle可能没用过,但是你不得不知道!

时间:2023-03-14 01:06:48 科技观察

本文转载自微信公众号《程序新视野》,作者为二哥。转载本文请联系程序新视界公众号。前言在使用Spring的过程中,我们通常会使用@PostConstruct和@PreDestroy在Bean初始化或销毁时进行一些操作,这些操作属于Bean生命周期层面。然后,有一些遗漏的场景。比如我们想对容器自身生命周期的事件(比如容器启停)做一些处理。一个典型的例子就是在SpringBoot中启动嵌入式Web容器。我应该怎么办?这就需要使用Spring提供的另一个接口Lifecycle。本文档介绍了Lifecycle接口和比它更智能的SmartLifecycle。Lifecycle接口Lifecycle是一个接口,它的作用是让开发者在所有的bean创建完成后(getBean)进行自己的初始化工作,或者在退出时进行资源销毁工作。生命周期定义了三个方法。任何实现了Lifecycle方法的Bean都会在ApplicationContext收到start、stop、restart等信号时调用相应的方法。因此,可以通过实现Lifecycle接口获取容器生命周期的回调,实现业务扩展。LifeCycle定义如下:publicinterfaceLifecycle{voidstart();voidstop();booleanisRunning();}自定义Lifecycle实现类首先我们定义一个实现Lifecycle接口的类,看看具体的实际效果:@ComponentpublicclassMyLifeCycleimplementsLifecycle{/***运行状态*/privatevolatilebooleanrunning=false;/***容器启动后调用*/@Overridepublicvoidstart(){System.out.println("容器启动后执行MyLifeCycle操作...");running=true;}/***容器停止时调用*/@Overridepublicvoidstop(){System.out.println("收到信号关闭容器MyLifeCycle操作...");running=false;}/***检查是否该组件正在运行。*1.只有当方法返回false时,才会执行start方法。*2.只有当该方法返回true时,才会执行stop(Runnablecallback)或stop()方法。*/@OverridepublicbooleanisRunning(){System.out.println("查看MyLifeCycle组件运行状态:"+running);returnrunning;}}简单的在SpringBoot项目中添加上面的代码,你会发现启动时并没有打印任何相关的日志,只有在应用关闭的时候才会打印出来:查看MyLifeCycle组件的运行状态:false这个是因为如果在SpringBoot或者Spring应用中没有调用AbstractApplicationContext#start方法,只是实现了Lifecycle接口,是不会执行Lifecycle接口中的startup方法和isRunning方法的。但是当应用程序退出时,会执行Lifecycle#isRunning方法判断Lifecycle是否已经启动,如果返回true,就会调用Lifecycle#stop()方法。这个例子有一个明显的问题,就是Lifecycle的接口方法只有在用户显式调用容器的start()和stop()方法后才会执行。在一般的项目中,我们很少显式地调用这个,所以我们需要一个更“聪明”的类来处理它,这就是SmartLifecycle。SmartLifecycleSmartLifecycle继承自Lifecycle,提供了更丰富的功能:首先,start()方法无需容器显式调用即可执行;其次,它可以控制多个SmartLifecycle实例的执行顺序。先看SmartLifecycle接口的源码:publicinterfaceSmartLifecycleextendsLifecycle,Phased{intDEFAULT_PHASE=2147483647;defaultbooleanisAutoStartup(){returntrue;}defaultvoidstop(Runnablecallback){this.stop();callback.run();}defaultintgetPhase(){returntrue;}defaultintgetPhase(){returntrue;}defaultintgetPhase(){returntrue;}defaultintgetPhase(){return可以看出,这个接口除了继承了Lifecycle接口,还继承了Phased。getPhase方法来自Phased。也是基于Phased接口的这个方法来控制SmartLifecycle的执行顺序。我们来看看示例代码:@ComponentpublicclassMySmartLifecycleimplementsSmartLifecycle{privatevolatilebooleanrunning=false;/***如果`Lifecycle`类的上下文正在调用`refresh`,希望自动回调,返回`true`的值,*false表示该组件旨在通过显式start()调用启动,类似于正常的生命周期实现。*/@OverridepublicbooleanisAutoStartup(){returntrue;}/***SmartLifecycle子类唯一方法,当isRunning方法返回true时,会调用该方法。*在很多框架的源码中,真正的逻辑都会写在stop()方法中。*如quartz、Redis的spring支持包。*/@Overridepublicvoidstop(Runnablecallback){System.out.println("MySmartLifecycle容器停止,执行回调函数");stop();//如果让isRunning返回true,需要执行stop方法,那就不要忘记调用回调.run()。//否则当程序退出时,Spring的DefaultLifecycleProcessor会认为MySmartLifecycle没有停止,程序会一直卡住无法结束,等待一定时间后自动结束(默认超时30秒).回调.run();}/***1.主要在该方法中启动任务或者其他异步服务,比如开启MQ接收消息
*2。当上下文被刷新时(所有对象都被实例化和初始化之后),这个方法会被调用,*默认的生命周期处理器会检查每个SmartLifecycle对象的isAutoStartup()方法返回的boolean值。*如果为“true”,将调用该方法,而不是等待显式调用它自己的start()方法。*/@Overridepublicvoidstart(){System.out.println("MySmartLifecycle容器启动完成...");running=true;}/***接口Lifecycle子类的方法,只有非SmartLifecycle子类才会执行该方法方法.
*1。该方法只对直接实现Lifecycle接口的类有效,对实现SmartLifecycle接口的类无效。
*2。方法stop()和方法stop(Runnablecallback)的区别在于后者是SmartLifecycle子类独有的。*/@Overridepublicvoidstop(){System.out.println("MySmartLifecycle容器停止...");running=false;}/***1.只有当这个方法返回false时,start方法才会被执行。
*2。只有当该方法返回true时,才会执行stop(Runnablecallback)或stop()方法。*/@OverridepublicbooleanisRunning(){System.out.println("MySmartLifecycle检查运行状态...");returnrunning;}/***如果有多个类实现了接口SmartLifecycle,则启动的执行顺序这些类是按照getPhase方法的返回值从小到大执行的。
*例如:1在2之前执行,-1在0之前执行。stop方法的执行顺序是相反的。getPhase返回值较大的类的stop方法先调用,返回值较小的类后调用。**/@OverridepublicintgetPhase(){return0;}}关于各个方法的作用,评论区已经解释清楚了,下面启动SpringBoot项目,打印日志如下:MySmartLifecyclecheckstherunningstatus...MySmartLifecyclecontainerstartup完成...关闭SpringBoot项目,打印日志如下:MySmartLifecycle检查运行状态...MySmartLifecycle容器停止,执行回调函数MySmartLifecycle容器停止...从上面的例子可以看出,如果一个Bean实现了SmartLifecycle接口,启动方法就会被执行。SmartLifecycle#isRunning判断是否执行过,返回false表示没有执行过,然后调用SmartLifecycle#start()执行。关机时,也是先检查运行状态,如果是运行,则执行关机操作。关闭时,也可以处理相应的回调函数。其中,Phased返回值越小,优先级越高。小结当需要根据Spring容器的生命周期来处理一些逻辑时,通常可以实现SmartLifecycle接口来完成。和SpringCloud一样,SpringBoot也有大量的实战案例。因此,无论实战还是阅读源码,不了解Lifecycle相关接口都是一种损失。这篇文章的产生,也是在遇到SpringCloud集成Nacos的源码时得到的启发。