如何在自定义事件发布后启动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(); } }
原理说明
- Profile条件化加载:
FoobarClass和AppLaunchWaiter都带有@Profile("foobar"),仅当该profile激活时才会被Spring容器加载;其他profile下这两个组件不会生效,应用按默认流程启动。 - SmartLifecycle启动顺序:Spring Boot的内嵌WebServer(如Tomcat)是通过
WebServerStartStopLifecycle组件启动的,它的phase为Integer.MIN_VALUE(最低优先级),会最后执行启动。而我们的AppLaunchWaiter设置了Integer.MAX_VALUE(最高优先级),会最先执行start()方法。 - 事件驱动等待:
AppLaunchWaiter的start()方法会进入阻塞,直到收到LaunchSpringAppEvent事件才继续执行。由于FoobarClass的@PostConstruct方法在所有SmartLifecycle组件启动前就已执行(容器刷新阶段的bean初始化环节),如果你的初始化逻辑是同步的,事件会提前触发,等待逻辑会直接通过;如果是异步初始化,等待逻辑会阻塞到事件发布后再放行,确保应用在初始化完成后才对外服务。
注意事项
- 一定要设置等待超时时间,避免因初始化失败导致应用无限阻塞。
- 如果你的初始化逻辑是异步的,确保异步任务完成后再发布事件,否则等待逻辑会提前放行。
- 构造注入
ApplicationEventPublisher比@Autowired更安全,避免循环依赖问题。
内容的提问来源于stack exchange,提问作者IceMajor
相关产品推荐
相关产品推荐

