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

如何在自定义事件发布后启动Spring Boot应用(仅foobar环境生效)

实现方案

完全可以实现,核心思路是利用Spring的SmartLifecycle接口控制应用启动时机,结合profile条件化加载组件,达到仅在foobar profile激活时延迟启动的效果。

步骤1:定义自定义事件

创建标记应用可以启动的事件类:

public class LaunchSpringAppEvent extends ApplicationEvent {
    public LaunchSpringAppEvent(String message) {
        super(message);
    }
}

步骤2:调整FoobarClass的事件发布

确保FoobarClass发布正确的自定义事件:

@Component
@Profile("foobar")
public class FoobarClass {

    private final ApplicationEventPublisher eventPublisher;

    // 构造注入替代@Autowired(更符合Spring规范)
    public FoobarClass(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    @PostConstruct
    public void postInit() {
        // 执行你的初始化逻辑(比如耗时的配置加载、资源初始化等)
        // ...

        // 发布事件,标记初始化完成
        eventPublisher.publishEvent(new LaunchSpringAppEvent("All done! Spring app can be launched now."));
    }
}

步骤3:实现启动等待组件

创建一个仅在foobar profile下生效的SmartLifecycle组件,负责等待自定义事件后再允许应用启动:

@Component
@Profile("foobar")
public class AppLaunchWaiter implements SmartLifecycle, ApplicationListener<LaunchSpringAppEvent> {

    private volatile boolean isReady = false;
    private volatile boolean isRunning = false;

    @Override
    public void start() {
        // 等待自定义事件触发,最多阻塞30秒避免无限等待
        synchronized (this) {
            try {
                if (!isReady) {
                    this.wait(30000);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("Wait for launch event was interrupted", e);
            }
        }

        if (!isReady) {
            throw new IllegalStateException("Timed out waiting for LaunchSpringAppEvent - initialization may have failed");
        }

        isRunning = true;
    }

    @Override
    public void stop() {
        isRunning = false;
    }

    @Override
    public boolean isRunning() {
        return isRunning;
    }

    @Override
    public void onApplicationEvent(LaunchSpringAppEvent event) {
        synchronized (this) {
            isReady = true;
            this.notify();
        }
    }

    // 设置最高优先级,确保在WebServer启动前执行等待逻辑
    @Override
    public int getPhase() {
        return Integer.MAX_VALUE;
    }

    @Override
    public boolean isAutoStartup() {
        return true;
    }

    @Override
    public void stop(Runnable callback) {
        stop();
        callback.run();
    }
}

原理说明

  1. Profile条件化加载:FoobarClass和AppLaunchWaiter都带有@Profile("foobar"),仅当该profile激活时才会被Spring容器加载;其他profile下这两个组件不会生效,应用按默认流程启动。
  2. SmartLifecycle启动顺序:Spring Boot的内嵌WebServer(如Tomcat)是通过WebServerStartStopLifecycle组件启动的,它的phase为Integer.MIN_VALUE(最低优先级),会最后执行启动。而我们的AppLaunchWaiter设置了Integer.MAX_VALUE(最高优先级),会最先执行start()方法。
  3. 事件驱动等待:AppLaunchWaiter的start()方法会进入阻塞,直到收到LaunchSpringAppEvent事件才继续执行。由于FoobarClass的@PostConstruct方法在所有SmartLifecycle组件启动前就已执行(容器刷新阶段的bean初始化环节),如果你的初始化逻辑是同步的,事件会提前触发,等待逻辑会直接通过;如果是异步初始化,等待逻辑会阻塞到事件发布后再放行,确保应用在初始化完成后才对外服务。

注意事项

  • 一定要设置等待超时时间,避免因初始化失败导致应用无限阻塞。
  • 如果你的初始化逻辑是异步的,确保异步任务完成后再发布事件,否则等待逻辑会提前放行。
  • 构造注入ApplicationEventPublisher比@Autowired更安全,避免循环依赖问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 18:14:58