除了Spring框架的常规事件(如ContextRefreshedEvent
)之外,SpringApplication
还会发送一些额外的应用事件。
注意:有些事件在应用上下文(ApplicationContext
)创建之前就已经被触发了。因此,你不能简单地将监听这些事件的监听器作为@Bean
来注册,因为在监听器需要响应事件时,应用上下文可能还没有完全初始化,从而无法访问到这些@Bean
。为了在早期阶段监听这些事件,你可以使用SpringApplication.addListeners(…)
方法或SpringApplicationBuilder.listeners(…)
方法来注册监听器。这些方法允许你在Spring应用启动过程中非常早的阶段就注册监听器,这样监听器就能捕获到那些在应用上下文创建之前触发的事件。
如果你希望这些监听器能够自动注册,无论应用是以何种方式创建的,你可以在你的项目中添加一个META-INF/spring.factories
文件,并使用org.springframework.context.ApplicationListener
键来引用你的监听器,如下例所示:
org.springframework.context.ApplicationListener=com.example.project.MyListener
在这个spring.factories
文件中,你可以为org.springframework.context.ApplicationListener
指定一个或多个监听器的全类名。当Spring Boot应用启动时,它会自动扫描这个文件,并实例化其中列出的所有监听器,然后将它们添加到应用上下文中。
这种方式的好处是,你不需要在应用代码中显式地添加监听器,无论你是使用SpringApplication.run()
方法还是通过其它方式创建应用上下文,监听器都会被自动注册。
当你的应用程序运行时,应用事件将按照以下顺序发送:
ApplicationStartingEvent
是在运行开始时发送的,但在进行任何处理之前,除了监听器和初始化器的注册之外。- 当上下文中要使用的
Environment
已知但上下文尚未创建时,会发送ApplicationEnvironmentPreparedEvent
。 - 当
ApplicationContext
已准备好并且已经调用了ApplicationContextInitializers
,但在加载任何bean定义之前,会发送一个ApplicationContextInitializedEvent
。 - 在刷新开始之前,但在加载了bean定义之后发送
ApplicationPreparedEvent
。 - 在上下文刷新后,但在调用任何应用和命令行运行器之前发送
ApplicationStartedEvent
。 - 发送
AvailabilityChangeEvent
事件,并紧接着使用LivenessState.CORRECT
来表明应用程序被认为是活动的。 - 在调用所有应用和命令行运行器之后发送
ApplicationReadyEvent
。 - 发送
AvailabilityChangeEvent
事件,并紧接着使用ReadinessState.ACCEPTING_TRAFFIC
来表明应用程序已准备好处理服务请求。 - 如果在启动过程中出现异常,则会发送
ApplicationFailedEvent
。
上述列表仅包括与SpringApplication
绑定的SpringApplicationEvent
。除此之外,在ApplicationPreparedEvent
之后和ApplicationStartedEvent
之前还会发布以下事件:
- 当
WebServer
准备就绪后,会发送WebServerInitializedEvent
。ServletWebServerInitializedEvent
和ReactiveWebServerInitializedEvent
分别是其Servlet和Reactive的变体。 - 当
ApplicationContext
被刷新时,会发送ContextRefreshedEvent
。
提示:你通常不需要使用应用程序事件,但知道它们的存在会很有用。在内部,Spring Boot使用事件来处理各种任务。
注意:事件监听器默认是在处理事件的同一线程中执行的,因此不应运行可能耗时的任务。相反,而是将这些任务放在应用程序运行器(Application Runner)或命令行运行器(Command-line Runner)中执行。
应用程序事件是通过使用Spring框架的事件发布机制来发送的。该机制的一部分确保发布给子上下文中的监听器的事件也会发布给任何祖先上下文中的监听器。因此,如果你的应用程序使用了多个层次的SpringApplication
实例,那么监听器可能会接收到多个相同类型的应用程序事件实例。
为了让你的监听器能够区分来自其自身上下文的事件和来自后代上下文的事件,它应该请求注入其应用上下文,然后将注入的上下文与事件的上下文进行比较。上下文可以通过实现ApplicationContextAware
接口来注入,或者,如果监听器是一个bean,也可以使用@Autowired
注解来注入。