在Spring应用中,有时需要在应用启动时执行某些初始化逻辑,并且这些逻辑只需执行一次。比如,初始化一些静态数据、加载配置文件或进行某些开销较大的操作。你可以通过多种方法实现这一需求:
使用
@PostConstruct
@PostConstruct
是Java EE中的一个注解,Spring也支持该注解。它标记的方法会在bean的依赖注入完成后自动调用。
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
@Component
public class MyInitializer {
@PostConstruct
public void init() {
// 在这里放置你的初始化逻辑
System.out.println("Application has started and this method is called once.");
使用 InitializingBean
接口
另一个方法是实现 InitializingBean
接口,其 afterPropertiesSet
方法将在所有属性设置后被调用。
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class MyInitializer implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
// 在这里放置你的初始化逻辑
System.out.println("Application has started and this method is called once.");
使用 @EventListener
监听 ContextRefreshedEvent
你也可以监听Spring上下文的事件,比如 ContextRefreshedEvent
,当上下文初始化或刷新时,该事件会触发。
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class MyInitializer {
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
// 在这里放置你的初始化逻辑
System.out.println("Application has started and this method is called once.");
使用 ApplicationRunner
或 CommandLineRunner
如果你使用的是Spring Boot,可以实现 ApplicationRunner
或 CommandLineRunner
接口,这两个接口用于在Spring Boot应用启动完成后执行代码。
使用 ApplicationRunner
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class MyInitializer implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// 在这里放置你的初始化逻辑
System.out.println("Application has started and this method is called once.");
使用 CommandLineRunner
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MyInitializer implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 在这里放置你的初始化逻辑
System.out.println("Application has started and this method is called once.");
以上几种方法都是在Spring应用启动时执行一次初始化逻辑的有效方式,根据你的需求和喜好选择适合的方式即可:
@PostConstruct
:简单直接,但只能在Spring管理的bean中使用。InitializingBean
:适合需要更明确生命周期控制的场景。@EventListener
:可以灵活地响应Spring上下文的各种事件。ApplicationRunner
/CommandLineRunner
:特别适用于Spring Boot项目,方便处理启动后的逻辑。
通过这些方法,你可以确保某些特定的初始化逻辑只在应用启动时执行一次。
在 Spring 应用中,有几种常见的方式可以实现应用启动时只执行一次的初始化逻辑。以下是几种常用的实现方法:
1. 使用 @PostConstruct 注解
import javax.annotation.PostConstruct;
@Component
public class StartupInitializer {
@PostConstruct
public void init() {
// 这个方法会在bean初始化完成后执行一次
System.out.println("应用启动时执行一次的逻辑");
- 简单直接
- 适用于单例Bean
- 方法会在依赖注入完成后执行
2. 实现 ApplicationListener 接口
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class StartupApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 确保只执行一次(因为可能有父子容器)
if (event.getApplicationContext().getParent() == null) {
System.out.println("应用上下文刷新完成后执行一次");
3. 使用 @EventListener 注解
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class StartupEventListener {
@EventListener
public void onApplicationEvent(ContextRefreshedEvent event) {
// 检查是否是根上下文,避免重复执行
if (event.getApplicationContext().getParent() == null) {
System.out.println("使用@EventListener执行启动逻辑");
4. 实现 CommandLineRunner 或 ApplicationRunner 接口
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class StartupRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
System.out.println("应用启动后执行一次,可以访问命令行参数");
或使用 ApplicationRunner:
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@Component
public class AppStartupRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("应用启动后执行一次,可以更方便地访问参数");
5. 使用 @Bean 的 initMethod 属性
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public MyService myService() {
return new MyService();
public class MyService {
public void init() {
System.out.println("Bean初始化时执行一次");
6. 使用 SmartLifecycle 接口(控制执行顺序)
import org.springframework.context.SmartLifecycle;
import org.springframework.stereotype.Component;
@Component
public class SmartLifecycleInitializer implements SmartLifecycle {
private volatile boolean running = false;
@Override
public void start() {
if (!running) {
System.out.println("SmartLifecycle启动时执行");
running = true;
@Override
public void stop() {
if (running) {
System.out.println("SmartLifecycle停止时执行");
running = false;
@Override
public boolean isRunning() {
return running;
@Override
public int getPhase() {
return 0; // 控制执行顺序,数字越小优先级越高
7. 使用 Spring Boot 的 ApplicationReadyEvent
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
@Component
public class ApplicationReadyListener {
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
System.out.println("应用完全准备好后执行一次");
执行顺序总结
@PostConstruct
方法InitializingBean.afterPropertiesSet()
@Bean
的 initMethod
指定方法ApplicationListener<ContextRefreshedEvent>
CommandLineRunner
/ApplicationRunner
ApplicationReadyEvent
监听器
最佳实践建议
- 简单初始化:使用
@PostConstruct
或 @EventListener(ContextRefreshedEvent.class)
- 需要访问命令行参数:使用
CommandLineRunner
或 ApplicationRunner
- 需要确保应用完全就绪:使用
ApplicationReadyEvent
- 需要控制初始化顺序:使用
SmartLifecycle
并设置 phase
值
- 避免在初始化方法中执行耗时操作,这会延迟应用启动
- 对于需要异步执行的初始化任务,考虑使用
@Async
- 确保初始化代码有适当的异常处理,防止应用启动失败
- 在分布式环境中,可能需要额外的机制确保初始化逻辑只执行一次
java@Component@Override// 只在根上下文初始化时执行System.out.println("根上下文初始化完成");防止多次触发(通过上下文判断)结合 @Async 实现异步初始化需求场景推荐方案简单初始化需要参数处理需要监听应用状态延迟执行@Scheduled复杂初始化流程自定义监听器 + 异步执行。
引言:有时候一个项目可能会通过不同配置实现多种功能,并且还需要启动1与启动2进行交互,每次打包又很麻烦,所以专门花了点时间,整理了下关于在idea下的springboot项目实现多启动的方法
public class Runner implements ApplicationRunner {
@Autowired
private ZkUserService zkUserService;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("======
它们的run()方法会在应用程序启动后自动调用,可以在这里实现需要在启动时执行的操作。在init()方法中可以执行只需要在启动时执行一次的操作,例如初始化一些数据、建立数据库连接等。@PostConstruct注解标记的方法会在Bean初始化完成后自动调用,可以在该方法中执行只需要在启动时执行一次的操作。如果需要多个操作在启动时执行,可以定义多个实现了CommandLineRunner或ApplicationRunner接口的Bean,并通过@Order注解指定它们的执行顺序。
此外,通过使用额外的注解参数,我们可以进一步定制启动注解的行为,例如指定组件扫描的基础包路径或排除特定的自动配置类。通过使用@EnableAutoConfiguration注解,我们可以充分利用Spring Boot提供的默认配置,减少手动配置的工作量。通过使用@ComponentScan注解,我们可以告诉Spring框架在指定的路径下查找和注册组件,使它们可以被其他组件引用和使用。在Spring Boot中,启动注解是每个应用程序的核心组件之一,它们帮助框架识别应用程序的入口点、配置信息和组件扫描路径。
如何在SpringBoot项目中使得job任务只在启动时执行一次?一般都会采用如下2种当时来实现:
1、编写一个类,实现CommandLineRunner,然后覆写run方法,将需要执行的代码块放在里面,当项目启动时只会执行一次。
2、在需要执行的方法上添加 @PostConstruct注解即可。
public class Runner implements ApplicationRunner {
@Autowired
private ZkUserService zkUserService;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("====
会先执行完多个ApplicationRunner 再执行多个CommandLineRunner。可以实现ApplicationRunner 或者 CommandLineRunner 接口。如果有多个ApplicationRunner 和 CommandLineRunner。查看spring boot 启动源码。