通常,Spring 应用启动后伴随着都会有些初始化操作,这些初始化操作通常有以下三种方式来实现
- 实现
ApplicationRunner
接口 - 实现
CommandLineRunner
接口 - 实现
ApplicationListener<ContextRefreshedEvent>
接口
ApplicationRunner 接口
方法签名
1 | public interface ApplicationRunner { |
调用时机
ApplicationRunner
的 run
方法会在应用启动后,在 ApplicationContext
被成功构建之后执行。这意味着在该时刻,所有的 bean 已经被初始化,并且应用上下文已经准备就绪
使用场景
适用于需要访问命令行参数的初始化逻辑。如果应用启动时需要使用命令行参数,可以使用 ApplicationRunner
来处理
CommandLineRunner 接口
方法签名
1 | public interface CommandLineRunner { |
调用时机
CommandLineRunner
的 run
方法也会在应用启动后,但在 ApplicationRunner
的 run
方法之前执行
使用场景
适用于简单的初始化逻辑,不需要访问复杂的命令行参数。如果只是需要执行一些简单的初始化工作,可以使用 CommandLineRunner
ApplicationListener<ContextRefreshedEvent> 接口
方法签名
1 | public interface ApplicationListener<E extends ApplicationEvent> extends EventListener { |
调用时机
ApplicationListener<ContextRefreshedEvent>
监听的是 Spring 上下文的刷新事件(ContextRefreshedEvent
),即在整个 Spring 上下文初始化完成后触发
使用场景
适用于更广泛的监听场景,不仅仅是应用启动时的初始化。如果需要监听 Spring 上下文刷新事件,无论何时发生,都可以使用该接口
Spring 上下文何时会刷新
Spring 上下文刷新事件(ContextRefreshedEvent
)在 Spring 容器初始化或刷新时触发。具体来说,当应用程序上下文被创建并初始化时,ContextRefreshedEvent
事件将被发布
Spring 上下文的初始化和刷新过程包括以下情况:
- 应用启动:当应用程序启动时,Spring 容器会初始化并加载配置,创建 bean 实例等。整个容器的初始化过程会触发上下文刷新事件
- 容器刷新:当调用
ApplicationContext
的refresh()
方法时,将触发容器的刷新过程。这通常在应用程序启动时发生,但也可以在运行时进行 - 子容器刷新:如果存在父子容器关系,当子容器初始化并刷新时,也会触发
ContextRefreshedEvent
事件 - 使用
ConfigurableApplicationContext
接口的refresh()
方法:如果应用程序使用的是ConfigurableApplicationContext
接口,并直接调用了refresh()
方法,将会触发上下文刷新事件
具体来说,ContextRefreshedEvent
事件的触发点是在 AbstractApplicationContext
类中的 finishRefresh()
方法中。在这个方法中,会发布 ContextRefreshedEvent
事件,通知所有注册的监听器(实现了 ApplicationListener
接口),以便它们可以执行与上下文刷新相关的初始化逻辑
1 | protected void finishRefresh() { |
所以,如果你想在 Spring 容器刷新完成后执行一些特定的初始化逻辑,可以使用 ApplicationListener
接口,实现 onApplicationEvent
方法,并在该方法中添加你的逻辑
需要刷新 Spring 上下文的场景
在 Spring 中,刷新 Spring 上下文(refresh()
方法)是一个显式的操作,通常是由 Spring 容器的用户(开发者)触发的。下面是一些常见的情况下可能需要手动刷新 Spring 上下文的场景:
启动时刷新: 在应用启动时,Spring 容器会自动执行初始化和刷新操作。这是最常见的场景,当应用启动时,容器会加载配置文件、初始化 bean,然后执行刷新操作
1
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
动态刷新: 在运行时需要对 Spring 容器进行动态的更新,例如,修改了配置文件,希望容器能够重新加载配置并应用更改
1
2
3ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// 执行一些修改配置的操作
context.refresh();Web 应用中的刷新: 在 Web 应用中,有时候可能需要手动刷新 Spring WebApplicationContext。这可以通过实现
ServletContextListener
接口,在contextInitialized
方法中调用refresh()
来实现1
2
3
4
5
6
7
8
9public class MyContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
WebApplicationContext context = ...
((ConfigurableApplicationContext) context).refresh();
}
// ...
}
通常情况下,不需要手动调用 refresh()
方法。Spring 容器会在初始化时自动刷新。手动调用 refresh()
的情况通常是在特殊的需求场景下,例如:动态修改配置并希望立即生效的情况。在普通的应用开发中,大多数情况下是由 Spring 容器自动执行刷新操作
三者差异
ApplicationRunner
的run
方法接收的参数是ApplicationArguments
,ApplicationArguments
提供了对命令行参数的更高级别的访问,包括解析和处理命令行参数。如果需要访问更丰富的命令行参数信息,可以选择使用ApplicationRunner
CommandLineRunner
的run
方法接收的参数是可变长度的字符串数组。如果只是进行一些简单的初始化工作而不需要复杂的命令行参数,可以选择使用CommandLineRunner
ApplicationListener
不仅仅是应用启动时的初始化,只要 Spring 上下文刷新事件发生,都会调用接口