很多时候我们需要在启动时就加载些资源,初始化一些内容。比如一些大数据的常用内容,或许要提前就加载到redis里,或者本地caffeine缓存里等各类实际场景的需求和做法,此时我们就需要考虑下什么时候该准备好
一:先说几个简单常见常用的
@PostConstruct 注解方式;实现 CommandLineRunner 接口方式
他们都是在 Spring Boot 应用程序启动时用于执行初始化逻辑的两种方法,但它们的用法和生命周期稍有不同
-
@PostConstruct:
@PostConstruct
是 Java 的标准注解,由 JSR-250 定义,用于在依赖注入完成后立即执行初始化方法。- 当 Spring 完成Bean的实例化、配置和注入以后,就会调用
@PostConstruct
标注的方法。 - 用于需要在该 Bean 初始化完成(即依赖注入完成)后立即执行的逻辑。
- 注解在类的一个方法上,该方法无参数且返回值为
void
。
@Component public class SomeComponent { @PostConstruct public void init() { // 初始化逻辑 } }
-
CommandLineRunner:
CommandLineRunner
是 Spring Boot 提供的一个接口,用于在 Spring Boot 应用程序启动后执行一些代码。- 应用程序启动并且 Spring 容器创建和刷新完成后,会执行实现
CommandLineRunner
接口的run
方法。 - 主要用于在应用程序启动之后,但在 Spring Boot 应用完全就绪之前(例如从命令行读取参数、执行启动时任务)。
@Component public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { // 初始化逻辑 } }
-
生命周期区别
@PostConstruct
在 Spring 容器创建和配置所有 Bean 的过程中发生的。此时Spring bean容器的所有bean还没有完全初始化并就绪CommandLineRunner
在所有 Spring Beans 被完全初始化并就绪后执行,但在 Spring Boot 应用完全就绪之前。这意味着它会在应用启动的最后阶段运行- 因此,@PostConstruct 方法会比 CommandLineRunner 更早执行
二:一些其他方式
-
ApplicationRunner:
- 用途: 与
CommandLineRunner
类似,但提供了更丰富的应用参数处理能力。 - 实现方式: 实现
ApplicationRunner
接口,并重写run(ApplicationArguments args)
方法。 - 代码示例:
import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) { // 获取应用程序参数 System.out.println("ApplicationRunner executed!"); } }
- 用途: 与
-
InitializingBean:
- 用途: 提供了一种基于接口的方式,在 Spring 完成 Bean 属性设置后执行自定义初始化逻辑,也就是依赖注入完成之后被调用
- 实现方式: 实现
InitializingBean
接口,并重写afterPropertiesSet()
方法。 - 代码示例:
import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; @Component public class MyInitializingBean implements InitializingBean { @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean executed after properties set!"); } }
-
@EventListener
注解 和ApplicationListener
:-
用途: 监听 Spring 应用上下文事件,如
ApplicationReadyEvent
,并在事件触发后执行逻辑。 -
@EventListener
实现方法:import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import org.springframework.boot.context.event.ApplicationReadyEvent; @Component public class MyEventListener { @EventListener public void onApplicationReady(ApplicationReadyEvent event) {//当然你也可以自定义一个方法名,参数类,比如继承ApplicationEvent的子类来实现业务关联的字段内容等 System.out.println("Application is ready!"); } }
-
ApplicationListener 实现方法:
import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; @Component public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { System.out.println("Context refreshed!"); } }
-
-
@Scheduled
:- 用途: 用于定时任务,但可以配置为在启动时执行。
- 实现方式: 配置一个只执行一次的定时任务。
- 代码示例:
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class MyScheduledTask { @Scheduled(initialDelay = 0, fixedDelay = Long.MAX_VALUE) public void onStartup() { System.out.println("Scheduled task executed on startup!"); } }
-
Bean Initialization Methods:
- 用途: 使用配置文件或注解指定 Bean 的初始化方法。
- 实现方式:
- XML 配置:
<bean id="myBean" class="com.example.MyBean" init-method="init"/>
- Java 配置:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean(initMethod = "init") public MyBean myBean() { return new MyBean(); } }
- XML 配置:
-
ApplicationContextInitializer
:- 用途: 在 Spring 应用上下文刷新之前进行初始化。
- 实现方式: 实现
ApplicationContextInitializer
接口,并在应用启动配置中注册。 - 代码示例:
import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; public class MyApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext context) { System.out.println("ApplicationContextInitializer executed!"); } }
- 注册方法(通常在
SpringApplication
中):import org.springframework.boot.SpringApplication; public class MySpringApplication { public static void main(String[] args) { SpringApplication app = new SpringApplication(MySpringApplication.class); app.addInitializers(new MyApplicationContextInitializer()); app.run(args); } }
三:简述下顺序
鉴于上面的内容,简单概述下部分顺序
-
Spring Boot 启动:
- Spring Boot 应用开始启动,启动类一般是一个带有
@SpringBootApplication
注解的类。
- Spring Boot 应用开始启动,启动类一般是一个带有
-
Spring 容器初始化:
- Spring 应用上下文(
ApplicationContext
)被创建并初始化。 - 所有 Spring Beans 被实例化、依赖注入、初始化的过程。
- 如果 某些 Bean 实现了
InitializingBean
接口或使用了@PostConstruct
注解,则执行相关初始化逻辑。
- Spring 应用上下文(
-
Commands 执行 (CommandLineRunner and ApplicationRunner):
CommandLineRunner
和ApplicationRunner
实例的run()
方法被调用。- 这是你可以在应用启动时执行定制逻辑的地方,比如清理或加载初始数据、启动后台任务等。
CommandLineRunner
接收从命令行传递到应用的参数,这些参数可以用于配置启动行为。
-
Spring Boot 应用准备就绪:
- 事件如
ApplicationReadyEvent
被发布,表示应用程序完全准备好接受请求或执行其他业务操作。
- 事件如
因此,CommandLineRunner
提供了一个合适的钩子,在你想在应用程序完全启动并准备好对外提供服务之前,执行自定义代码或准备工作时使用。
简单常用的基本是@PostConstruct,CommandLineRunner或ApplicationRunner