web-dev-qa-db-ja.com

プログラムでSpring Bootアプリケーションを再起動する/ Springコンテキストを更新する

ユーザーが介入しなくても、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の問題である場合に備えて両方を試みました)、何も起こりません。

私も プログラムでSpring Bootアプリケーションを再起動する を見ましたが、それらの答えからは何もうまくいかないようです。さらに、_/restart_エンドポイントがあると思われるSpring Actuatorを調べましたが、それはもう存在しないようです。

9
Crembo

アレックスの解決策は機能しますが、2つの追加の依存関係(Actuatorおよび_Cloud Context_)を含めるだけでは信じられません1つの操作を実行できます。代わりに、私は彼の答えを組み合わせて、自分のやりたいことをするためにコードを変更しました。

したがって、まず最初に、new Thread()setDaemon(false);を使用してコードが実行されるのはcrucialです。再起動を処理する次のエンドポイントメソッドがあります。

_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

それが誰かを助けるかもしれない場合のために、ここにぷらJava Cremboの受け入れられた答えの翻訳があります。

コントローラー方式:

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

}
7

RestartEndPointspring-cloud-context依存関係内)を使用して、Spring Bootアプリケーションをプログラムで再起動できます。

@Autowired
private RestartEndpoint restartEndpoint;

...

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

これは機能しますが、メモリリークが発生する可能性があることを通知する例外がスローされます。

Webアプリケーション[xyx]は[Thread-6]という名前のスレッドを開始したようですが、停止できませんでした。これにより、メモリリークが発生する可能性が非常に高くなります。スレッドのスタックトレース:

同じ答えがこの別の質問に提供されました(別の言い方をすると)Java関数

3
alexbt

以下の再起動方法が機能します。

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

} `

0
Chetan Laddha

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

わたしにはできる。この助けを願っています。

0
Lucifer Nick