SpringBoot应用关闭时发什么了啥?SpringApplicationShutdownHook是什么?
SpringBoot应用关闭时发什么了啥?
- 这个SpringApplicationShutdownHook是做什么的?
- 我们如何去实现这种优雅的资源释放和状态清理呢?
- 通过实现DisposableBean
- 在Bean的方法使用@PerDstroy
- 解释
- 没有实现DisposableBean或者@PerDestroy又是如何释放资源的呢?
我们用Springboot开发了很多东西,但是你有没有关注过SpringBoot应用在关闭时都做了啥?
有没有在关闭应用时候,看到过
[SpringApplicationShutdownHook] INFO xxxxxx
这个SpringApplicationShutdownHook是做什么的?
它是Spring Boot框架中用于在应用关闭时执行清理工作的一种机制。当Java虚拟机(JVM)接收到关闭信号,无论是通过正常关机、系统中断还是其他方式,它都会启动注册过的所有shutdown hook。
在Spring Boot应用中,SpringApplicationShutdownHook是一个特殊的钩子,由Spring框架自动注册到JVM的shutdown hook队列中。当JVM开始关闭时,这个钩子会被触发,并执行一系列清理操作,如:
- 关闭Spring应用上下文(ApplicationContext),这会触发容器内所有bean的销毁生命周期方法。
- 释放资源,比如数据库连接、网络连接或文件句柄等。
- 清理日志资源,确保所有的日志信息都已经正确写入到日志文件中。
- 执行任何用户自定义的shutdown回调逻辑,例如实现DisposableBean接口的bean或使用@PreDestroy注解的方法。
总之,SpringApplicationShutdownHook的存在是为了确保应用在退出前能够进行优雅的资源释放和状态清理,从而避免内存泄漏或其他未预期的问题。
我们如何去实现这种优雅的资源释放和状态清理呢?
通过实现DisposableBean
/**
* @author by Guoshun
* @version 1.0.0
* @description DisposableBean
* @date 2024/3/18 13:59
*/
@Component
public class DisposableBeanTest implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("This is DisposableBean-Test");
}
}
在Bean的方法使用@PerDstroy
/**
* @author by Guoshun
* @version 1.0.0
* @description
* @date 2024/3/18 14:05
*/
@Component
public class PreDestroyTest {
@PreDestroy
public void destroy(){
System.out.println("This is PreDestroyTest method @PerDestroy");
}
}
解释
“关闭Bean”通常指的是当Spring容器(ApplicationContext)关闭时,正确地销毁那些实现了DisposableBean接口或者在其上定义了@PreDestroy注解的方法的bean,以确保它们可以释放资源。
在Spring框架中,容器管理的bean生命周期包括初始化和销毁两个阶段:
- 初始化:Spring会根据配置或注解调用bean的初始化方法,如@PostConstruct注解的方法。
- 销毁:当Spring容器关闭时,它会自动调用bean的销毁方法,如果bean实现了DisposableBean接口,那么会调用其destroy()方法;另外,如果在bean的方法上使用了@PreDestroy注解,这个方法也会在bean销毁前被调用。
在Spring Boot应用中,如果你需要在应用停止时执行某些清理操作,可以按照上述方式来编写Bean的销毁逻辑。对于Web应用而言,可以通过监听ApplicationListener事件来实现更复杂的关闭行为。
需要说明的是:请不要用kill -9 pid 形式去结束SpringBoot应用。该命令会立即终止进程,可不管你什么shoutdown hook。真的到了这一步请使用 kill -15 pid
没有实现DisposableBean或者@PerDestroy又是如何释放资源的呢?
在Spring框架中,即使bean没有实现DisposableBean接口或使用@PreDestroy注解,Spring容器仍然会在应用关闭时尝试释放资源。Spring通过以下几种机制来确保非显式定义销毁方法的bean也能正确地清理资源:
- 依赖注入(DI)的自动管理: Spring容器对所有由它创建和管理的bean具有生命周期控制能力。当容器关闭时,会逐个销毁这些bean。对于那些没有自定义销毁逻辑的bean,尽管不会执行特定的销毁方法,但Spring仍会进行一些基本的资源清理,如解除自动注入的引用、取消监听器注册等。
- 依赖的JVM垃圾回收: 对于非容器管理的原始资源(例如直接new出来的对象),如果它们被Spring托管的bean持有引用,则在Spring容器内这些bean被销毁后,由于GC机制,这些非托管资源也可能在适当的时机被垃圾回收。
- 数据库连接池和其他资源库: 很多资源库,如数据库连接池,本身提供了资源管理的功能。当Spring容器关闭时,通常会从上下文关闭回调中触发资源库的关闭操作,从而导致连接池关闭并释放所有连接。
- 基于配置的清理策略: 在Spring Boot中,可以通过配置类或者application.properties/yml文件指定资源的关闭行为,比如数据源、缓存等,框架会根据配置在容器关闭时自动调用相应的清理逻辑。
综上所述,虽然不是所有的资源都需要实现DisposableBean或使用@PreDestroy来手动清理,但在大多数情况下,Spring框架通过其自身的生命周期管理以及与第三方库的良好集成,能够有效地处理大部分资源的释放问题。当然,对于需要特殊处理的资源释放逻辑,最佳实践仍然是通过上述两种方式显式声明。
其他优秀的文章:值得一读