优质博文:IT-BLOG-CN
一、使用spring容器的close方法关闭。
可通过在代码中获取SpringContext并调用close方法去关闭容器。
使用SpringApplication的exit方法。
public static int exit(ApplicationContext context,
ExitCodeGenerator... exitCodeGenerators) {
Assert.notNull(context, "Context must not be null");
int exitCode = 0;
try {
try {
//获取ExitCodeGenerator的Bean并用ExitCodeGenerators管理
ExitCodeGenerators generators = new ExitCodeGenerators();
Collection<ExitCodeGenerator> beans = context
.getBeansOfType(ExitCodeGenerator.class).values();
generators.addAll(exitCodeGenerators);
generators.addAll(beans);
exitCode = generators.getExitCode();
if (exitCode != 0) {
// 发布ExitCodeEvent事件
context.publishEvent(new ExitCodeEvent(context, exitCode));
}
}
finally {
close(context);
}
}
catch (Exception ex) {
ex.printStackTrace();
exitCode = (exitCode != 0) ? exitCode : 1;
}
return exitCode;
}
上述代码,总的来说就是,获取ExitCodeGenerator的Bean并用ExitCodeGenerators管理, 注意getExitCode()的实现是取ExitCodeGenerator集合中最大的exitCode作为最终exitCode,
最后,关闭容器。
二、exitCode
SpringApplication#exit方法返回的exitCode还需要自行调用System#exit方法去指定。 该System#exit(int code)的参数,能被父进程获取并使用。一般按照惯例0为程序正常退出,非0位不正常退出。 我写了的运行demo:
@Slf4j
@SpringBootApplication
public class ApplicationMainShutDownBySpringApplication {
public static void main(String[] args) {
ConfigurableApplicationContext ctx = new SpringApplicationBuilder(ApplicationMainShutDownBySpringApplication.class).build().run(args);
int exitCode = SpringApplication.exit(ctx);
log.info("exitCode is {}!", exitCode);
System.exit(exitCode);
}
@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 10;
}
}
执行window bat:
java -jar spring-boot-mvc-shutdown-demo.jar & echo %ERRORLEVEL%
省略其他日志最终输出:
10
可以看最终输出是:10。
二、使用Actuator的shutdown http接口或JMX
可参考Actuator包的ShutdownEndpoint,实质上是调用spring容器的close方法关闭的。
http方式关闭:
JMX方式关闭:
三、kill进程
一般的kill(kill -15)会触发应用在refreshContext时(并且SpringApplication实例的registerShutdownHook为true时)加上的注册到JVM的shutdownhook。
public void registerShutdownHook() {
if (this.shutdownHook == null) {
// No shutdown hook registered yet.
this.shutdownHook = new Thread() {
@Override
public void run() {
synchronized (startupShutdownMonitor) {
doClose();
}
}
};
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
四、pidFile生成
spring boot提供ApplicationPidFileWriter类,将运行时pid写入指定文件中。
应用场景:可供kill使用。kill $(cat application.pid)
添加步骤:
【1】增加监听器
org.springframework.context.ApplicationListener=\
org.springframework.boot.context.ApplicationPidFileWriter
【2】配置项
spring:
pid:
fail-on-write-error: true
#可通过此文件里的pid去关闭应用
file: ./application.pid
五、spring容器close代码分析
这里对容器关闭进行一些分析,以注释的形式写在下面。
/**
* Close this application context, destroying all beans in its bean factory.
* <p>Delegates to {@code doClose()} for the actual closing procedure.
* Also removes a JVM shutdown hook, if registered, as it's not needed anymore.
* @see #doClose()
* @see #registerShutdownHook()
*/
@Override
public void close() {
synchronized (this.startupShutdownMonitor) {
//委托给钩子方法doClose去做
doClose();
// If we registered a JVM shutdown hook, we don't need it anymore now:
// We've already explicitly closed the context.
if (this.shutdownHook != null) {
try {
//去掉shutdown hook
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}
catch (IllegalStateException ex) {
// ignore - VM is already shutting down
}
}
}
}
protected</