You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何以编程方式优雅退出Micronaut应用程序

问题描述

我想要以编程方式退出Micronaut应用程序。我知道使用PicocliRunner可以实现这一点,但不清楚两者的差异所在。添加cli环境也没有改变现状。以下是我尝试的代码:

public class Application {
    public static void main(String[] args) {
        // MICRONAUT_ENVIRONMENTS=cli
        ApplicationContext run = Micronaut.run(Application.class, args);
        try {
            EmbeddedServer server = run.getBean(EmbeddedServer.class);
            HttpClient httpClient = HttpClient.create(server.getURL());
            String s = Mono.from(httpClient.retrieve("/hello")).blockOptional().orElseThrow();
            System.out.println(s); // prints 'world'
        } catch (Exception e) {
            e.printStackTrace();
        }

        run.close(); // .stop();, .destroyBean(run);
        System.out.println("closed"); // jvm does not exit
    }

    @io.micronaut.http.annotation.Controller
    public static class Controller {
        @Get("/hello")
        String get() {
            return "world";
        }
    }
}
原因分析与解决方案

问题核心是你手动创建的HttpClient未被关闭,其后台线程仍在运行,导致JVM无法正常退出。同时ApplicationContext.close()仅负责容器自身的关闭,不会自动清理所有外部创建的资源。

修复方案

1. 显式关闭HttpClient

将HttpClient的关闭逻辑放在finally块中,确保无论是否出现异常都能执行:

public class Application {
    public static void main(String[] args) {
        // MICRONAUT_ENVIRONMENTS=cli
        ApplicationContext run = Micronaut.run(Application.class, args);
        HttpClient httpClient = null;
        try {
            EmbeddedServer server = run.getBean(EmbeddedServer.class);
            httpClient = HttpClient.create(server.getURL());
            String s = Mono.from(httpClient.retrieve("/hello")).blockOptional().orElseThrow();
            System.out.println(s); // prints 'world'
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (httpClient != null) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        run.close();
        System.out.println("closed");
    }

    @io.micronaut.http.annotation.Controller
    public static class Controller {
        @Get("/hello")
        String get() {
            return "world";
        }
    }
}

2. 依赖注入HttpClient(推荐)

避免手动创建HttpClient,改为通过Micronaut容器注入,容器关闭时会自动管理其生命周期:

public class Application {
    public static void main(String[] args) {
        // MICRONAUT_ENVIRONMENTS=cli
        ApplicationContext run = Micronaut.run(Application.class, args);
        try {
            EmbeddedServer server = run.getBean(EmbeddedServer.class);
            HttpClient httpClient = run.getBean(HttpClient.class, server.getURL());
            String s = Mono.from(httpClient.retrieve("/hello")).blockOptional().orElseThrow();
            System.out.println(s); // prints 'world'
        } catch (Exception e) {
            e.printStackTrace();
        }

        run.close();
        System.out.println("closed");
    }

    @io.micronaut.http.annotation.Controller
    public static class Controller {
        @Get("/hello")
        String get() {
            return "world";
        }
    }
}

3. 兜底:强制退出JVM

如果上述方法仍无法退出,可在run.close()后调用System.exit(0)强制终止JVM,但这是最后手段,优先保证资源正常释放。

PicocliRunner的差异

PicocliRunner是为命令行场景设计的,命令执行完成后会自动触发完整的清理流程:

  • 关闭ApplicationContext
  • 终止所有关联的后台线程
  • 调用System.exit()确保JVM退出

而直接使用Micronaut.run()启动的应用默认是面向长期运行的服务(如Web应用)设计的,不会自动触发JVM退出逻辑,必须手动清理所有资源。

内容的提问来源于stack exchange,提问作者Dave Ankin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 12:01:08