如何在线重启 SpringBoot 应用程序

如何在线重启 SpringBoot 应用程序

1.场景

在测试项目或者生产项目中,由于修改了一些配置参数文件或者必须需要重启,我们此时可以不需要通过运维打包发布,可以实现快速优雅的重启。
就如同Halo项目一样,在线修改application.yml文件后,可以在线重启。如下图
image.png

2.原理

核心类为:ConfigurableApplicationContext,它是SpringApplication.run(Application.class,args)运行的返回对象

  1. 主程序启动时,通过static ConfigurableApplicationContext常量保存SpringApplication.run(Application.class,args)的返回值
  2. 从ConfigurableApplicationContext获取Bean(ApplicationArguments)对象
  3. 启动一个非Damon线程
  4. 调用ConfigurableApplicationContext.close()方法,关闭当前应用的上下文
  5. 重新运行SpringApplication.run(Application.class,args)方法,并将返回值赋值给static ConfigurableApplicationContext
  6. 重启成功

3.code

3.1 main方法

@SpringBootApplication
public class Application{

    private static ConfigurableApplicationContext context;

    public static void main(String[] args) {
        context = SpringApplication.run(Application.class, args);
    }


    public static void restart() {
        ApplicationArguments args = context.getBean(ApplicationArguments.class);

        Thread thread = new Thread(() -> {
            context.close();
            context = SpringApplication.run(Application.class, args.getSourceArgs());
        });

        thread.setDaemon(false);
        thread.start();
    }

}

3.2 controller方法

@RestController
public class RestartController {
    @Value("${spring.application.name}")
    String name;
 
    private static Logger logger = LoggerFactory.getLogger(RestartController.class);
 
    @RequestMapping("/info")
    public String info() {
        logger.info("spring.application.name:" + name);
        return name;
    }
    @RequestMapping("/restart")
    public void restart() {
	//此处修改spring.application.name只为验证是否重启成功,实际可去除	
    	  try {
    		  PropUtil instance = PropUtil.getProp("application.properties");
              instance.setProperty("spring.application.name", "Fredia.wang");
    	    } catch (Exception e) {
    	        e.printStackTrace ( );
    	    }
	//重启	
    	Application.restart();
    
    }
}

3.3 propertis工具

/**
 * properties 工具
 */
public class PropUtil {
    private static PropertiesConfiguration propConfig = null;

    private static final PropUtil PROP_UTIL = new PropUtil();

    private PropUtil() {
    }

    public static PropUtil getProp(String propertiesFile) {
        //执行初始化
        init(propertiesFile);
        return PROP_UTIL;
    }

    private static void init(String propertiesFile) {
        try {
            propConfig = new PropertiesConfiguration(propertiesFile);
            //自动重新加载
            propConfig.setReloadingStrategy(new FileChangedReloadingStrategy());
            //自动保存
            propConfig.setAutoSave(true);
            propConfig.save();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    public Object getValue(String key) {
        return propConfig.getProperty(key);
    }


    public void setProperty(String key, String value) {
        propConfig.setProperty(key, value);
    }

}

3.4 验证步骤

  1. 启动项目后,先访问项目/info,确认当前项目名称
  2. 访问/restart接口,控制台验证
  3. 再次访问/info,确认项目名称是否变更
    验证图如下
    初始项目名
    重启后发现JVM并未重启
    重启后项目名

4.总结

理论上,生产项目是不能如此用这个方法的。测试环境或者预生产环境,在紧急情况下,尤其是在k8s体系下想节省编译打tag的情况下,可以适度使用。既然是spring官方提供的方法,肯定有其用武之地。项目的配置和管理都在向可视化,简单化发展。核心技术在逐渐下沉的时代,技术人员其实应该对自我的要求更高了。“只知其然,而不知其所以然”是一件恐怖的事。

Copyright: 采用 知识共享署名4.0 国际许可协议进行许可

Links: https://blog.wyatt.plus/?p=66

Buy me a cup of coffee ☕.