我正在尝试以编程方式重新启动我的 Spring 应用程序,而无需用户干预。
基本上,我有一个页面允许切换应用程序的模式(实际上意味着切换当前活动的配置文件),据我所知,我必须重新启动上下文。
目前我的代码非常简单,只是为了重启(顺便说一句,这是 Kotlin):
context.close()
application.setEnvironment(context.environment)
ClassUtils.overrideThreadContextClassLoader(application.javaClass.classLoader)
context = application.run(*argsArray)
但是,当我这样做时,JVM 立即存在。我也尝试过
context.close()
但这似乎只是杀死了 Tomcat/Jetty(都尝试了以防万一是 Tomcat 问题),然后什么也没有发生。我还看到了以编程方式重新启动 Spring Boot 应用程序
,但从这些答案中似乎没有任何效果。此外,我研究了 Spring Actuator,据说它有 context.refresh()
端点,但它似乎不再存在了?
,但我不相信仅仅为了能够执行一项操作而包含 2 个额外的依赖项(/restart
和
Actuator
)。相反,我结合了他的答案并修改了我的代码,以便做我想做的事。因此,首先,使用
Cloud Context
和 new Thread()
执行代码是
至关重要的。我有以下处理重启的端点方法:
setDaemon(false);
val restartThread = Thread {
logger.info("Restarting...")
Thread.sleep(1000)
SpringMain.restartToMode(AppMode.valueOf(change.newMode.toUpperCase()))
logger.info("Restarting... Done.")
}
restartThread.isDaemon = false
restartThread.start()
不是必需的,但我希望我的控制器在实际重新启动应用程序之前输出视图。
Thread.sleep(1000)
具有以下特点:
SpringMain.restartToMode
启动应用程序时,
@Synchronized fun restartToMode(mode: AppMode) {
requireNotNull(context)
requireNotNull(application)
// internal logic to potentially produce a new arguments array
// close previous context
context.close()
// and build new one using the new mode
val builder = SpringApplicationBuilder(SpringMain::class.java)
application = builder.application()
context = builder.build().run(*argsArray)
}
和
context
来自application
方法:main
我不完全确定这是否会产生任何问题。如果有的话,我会更新这个答案。希望这对其他人有帮助。
控制器方法:
val args = ArrayList<String>()
lateinit var context: ConfigurableApplicationContext
lateinit var application: SpringApplication
@Throws(Exception::class)
@JvmStatic fun main(args: Array<String>) {
this.args += args
val builder = SpringApplicationBuilder(SpringMain::class.java)
application = builder.application()
context = builder.build().run(*args)
}
主类(仅有效位):
@GetMapping("/restart")
void restart() {
Thread restartThread = new Thread(() -> {
try {
Thread.sleep(1000);
Main.restart();
} catch (InterruptedException ignored) {
}
});
restartThread.setDaemon(false);
restartThread.start();
}
如果您启用了开发工具,它只会工作一次。第二次获得 NPE
可以通过让重新启动线程使用与初始主调用线程相同的类加载器来避免此 NPE:
private static String[] args;
private static ConfigurableApplicationContext context;
public static void main(String[] args) {
Main.args = args;
Main.context = SpringApplication.run(Main.class, args);
}
public static void restart() {
// close previous context
context.close();
// and build new one
Main.context = SpringApplication.run(Main.class, args);
}
private static volatile ConfigurableApplicationContext context;
private static ClassLoader mainThreadClassLoader;
public static void main(String[] args) {
mainThreadClassLoader = Thread.currentThread().getContextClassLoader();
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.setContextClassLoader(mainThreadClassLoader);
thread.setDaemon(false);
thread.start();
}
然后使用org.springframework.boot.devtools.restart.Restarter 称之为:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
这对我有用。希望这有帮助。
@Autowired
private RestartEndpoint restartEndpoint;
...
Thread restartThread = new Thread(() -> restartEndpoint.restart());
restartThread.setDaemon(false);
restartThread.start();
如果您启用了开发工具,它只会工作一次。第二次获得 NPE