以编程方式重新启动Spring Boot应用程序/刷新Spring Context

时间:2016-09-08 08:31:52

标签: java spring spring-mvc spring-boot kotlin

我正在尝试以编程方式重新启动Spring应用程序,而无需用户进行干预。

基本上,我有一个允许切换应用程序模式的页面(实际上意味着切换当前活动的配置文件),据我所知,我必须重新启动上下文。

目前我的代码非常简单,它只适用于重启位(顺便说一下,这是Kotlin):

    context.close()
    application.setEnvironment(context.environment)
    ClassUtils.overrideThreadContextClassLoader(application.javaClass.classLoader)
    context = application.run(*argsArray)

然而,当我做context.close()时,JVM立即存在。我也尝试了context.refresh(),但这似乎只是简单地杀死了Tomcat / Jetty(只是因为它是一个Tomcat问题而尝试过)然后没有任何反应。

我也见过Programmatically restart Spring Boot application但似乎从这些答案中找不到任何东西。此外,我查看了Spring Actuator,它应该具有/restart端点,但那似乎不再存在了吗?

非常感谢帮助。感谢。

6 个答案:

答案 0 :(得分:10)

即使Alex的解决方案有效,我也不相信包含2个额外的依赖项(ActuatorCloud 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)
}

启动应用程序时,contextapplication来自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)
}

我不完全确定这是否会产生任何问题。如果有的话,我会更新这个答案。希望这对其他人有任何帮助。

答案 1 :(得分:6)

如果它可以帮助某人,这里是Crembo接受的答案的pura Java翻译。

控制器方法:

sed -n "$dateRange" "$error_log_file_name"|egrep -i "($query_value)"|grep -E -i -v "$not_query_value"|head -n $first_few_lines_value>>"$extracted_log_path_value"

主类(仅限有效位):

@GetMapping("/restart")
void restart() {
    Thread restartThread = new Thread(() -> {
        try {
            Thread.sleep(1000);
            Main.restart();
        } catch (InterruptedException ignored) {
        }
    });
    restartThread.setDaemon(false);
    restartThread.start();
}

答案 2 :(得分:3)

您可以使用RestartEndPoint(在spring-cloud-context依赖项中)以编程方式重新启动Spring Boot应用程序:

@Autowired
private RestartEndpoint restartEndpoint;

...

Thread restartThread = new Thread(() -> restartEndpoint.restart());
restartThread.setDaemon(false);
restartThread.start();

它可以工作,即使它会抛出一个异常通知你这可能会导致内存泄漏:

  

Web应用程序[xyx]似乎已经启动了一个名为的线程   [Thread-6]但未能阻止它。这很有可能创造一个   内存泄漏。堆栈线程跟踪:

为其他问题提供了相同的答案(措辞不同)Call Spring actuator /restart endpoint from Spring boot using a java function

答案 3 :(得分:2)

正如已经评论过的,由于上下文为空,因此之前给出的通过线程重新启动实现只能工作一次,而第二次抛出NPE则可行。

可以通过使重新启动线程使用与初始主调用线程相同的类加载器来避免此NPE:

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();
}

答案 4 :(得分:0)

我已经通过使用Spring Devtools中的Restarter解决了这个问题。 将此添加到pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
</dependency>

然后使用org.springframework.boot.devtools.restart.Restarter 叫这个:

Restarter.getInstance().restart();

对我有用。希望有帮助。

答案 5 :(得分:0)

下面的重新启动方法将起作用。

`@SpringBootApplication 公共类申请{

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();
}

}`