ユーザーが介入しなくても、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を調べましたが、それはもう存在しないようです。
アレックスの解決策は機能しますが、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)
}
_
これで問題が発生するかどうかは完全にはわかりません。ある場合は、この回答を更新します。うまくいけば、これは他の人の助けになるでしょう。
それが誰かを助けるかもしれない場合のために、ここにぷら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);
}
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]という名前のスレッドを開始したようですが、停止できませんでした。これにより、メモリリークが発生する可能性が非常に高くなります。スレッドのスタックトレース:
同じ答えがこの別の質問に提供されました(別の言い方をすると): Java関数
以下の再起動方法が機能します。
`@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();
}
} `
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();
わたしにはできる。この助けを願っています。