it-roy-ru.com

Программно перезапустить приложение Spring Boot/Обновить контекст Spring

Я пытаюсь программно перезапустить приложение Spring без вмешательства пользователя.

По сути, у меня есть страница, которая позволяет переключать режим приложения (фактически означает переключение текущего активного профиля), и, насколько я понимаю, я должен перезапустить контекст.

В настоящее время мой код очень прост, он просто для перезапуска (это, кстати, Котлин):

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

Однако в тот момент, когда я выполняю context.close(), JVM существует немедленно. Я также пробовал context.refresh(), но это, кажется, просто убивает Tomcat/Jetty (пробовал оба на тот случай, если это была проблема Tomcat), а затем ничего не происходит.

Я также видел программно перезапустить приложение Spring Boot но, похоже, мне ничего не помогло из этих ответов. Кроме того, я посмотрел на Spring Actuator, который предположительно имеет конечную точку /restart, но, похоже, его там больше нет?

Помощь будет принята с благодарностью. Благодарю.

9
Crembo

Несмотря на то, что решение Алекса works, я не верю, что нужно включить 2 дополнительные зависимости (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)
}

Я не совсем уверен, вызывает ли это какие-либо проблемы. Если будет, я обновлю этот ответ. Надеюсь, это поможет другим.

10
Crembo

В случае, если это может кому-то помочь, вот перевод pura Java принятого ответа Крембо.

Контроллер метод:

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

Основной класс (только значащие биты):

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

}
6
Olivier Gérardin

Вы можете использовать RestartEndPoint (в зависимости spring-cloud-context) для программного перезапуска приложения Spring Boot:

@Autowired
private RestartEndpoint restartEndpoint;

...

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

Он работает, даже если он выдаст исключение, чтобы сообщить вам, что это может привести к утечкам памяти:

Похоже, что веб-приложение [xyx] запустило поток с именем [Тема-6], но не смог остановить это. Это очень вероятно, чтобы создать утечка памяти. След стека потока:

Тот же ответ был предоставлен для этого другого вопроса (сформулированного по-другому): Вызовите исполнительный элемент Spring/перезапустите конечную точку из Spring, используя функцию Java

3
alexbt

Я решил эту проблему с помощью Restarter из Spring Devtools . Добавьте это в 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();

Меня устраивает. Надеюсь, это поможет.

0
Lucifer Nick