知識庫 / Spring / Spring Boot RSS 訂閱

在 Spring 啓動時運行邏輯指南

Spring Boot,Spring Persistence
HongKong
4
02:46 PM · Dec 06 ,2025

1. 概述

在本教程中,我們將重點介紹如何在 Spring 應用程序的啓動時執行邏輯。

2. 在啓動時執行邏輯

在 Spring 應用程序啓動期間或之後執行邏輯是一種常見的場景。但它也常常導致多種問題。

為了從反控制原則中獲益,我們需要放棄對應用程序流程的某些控制權,特別是啓動時實例化、設置邏輯等,需要特別關注。

我們不能簡單地將我們的邏輯包含在 Bean 的構造函數中,也不能在實例化任何對象之後調用方法,因為在這些過程中我們無法控制流程。

下面來看一個實際的例子:

@Component
public class InvalidInitExampleBean {

    @Autowired
    private Environment env;

    public InvalidInitExampleBean() {
        env.getActiveProfiles();
    }
}

在這裏,我們試圖在構造函數中訪問一個 自動注入 字段。當構造函數被調用時,Spring Bean 尚未完全初始化。這會導致問題,因為 訪問尚未初始化字段會導致 NullPointerExceptions.

下面我們將探討 Spring 提供的幾種處理這種情況的方法。

2.1. @PostConstruct 註解

我們可以使用 Javax 的 @PostConstruct 註解來標註一個在 Bean 初始化後立即執行的方法。請注意,Spring 即使沒有需要注入的依賴,也會運行標註的方法。

以下是 @PostConstruct 註解的實際應用:

@Component
public class PostConstructExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(PostConstructExampleBean.class);

    @Autowired
    private Environment environment;

    @PostConstruct
    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

我們能看到,Environment 實例安全地注入,然後在帶有 @PostConstruct 註解的方法中被調用,而沒有拋出 NullPointerException

2.2. <em>InitializingBean</em> 接口

採用 <em>InitializingBean</em> 方法的工作方式與之前類似。 區別在於,我們無需註解方法,而是需要實現 <em>InitializingBean</em> 接口以及 <em>afterPropertiesSet()</em> 方法。

以下是如何使用 <em>InitializingBean</em> 接口實現之前的示例:

@Component
public class InitializingBeanExampleBean implements InitializingBean {

    private static final Logger LOG 
      = Logger.getLogger(InitializingBeanExampleBean.class);

    @Autowired
    private Environment environment;

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.3. ApplicationListener

我們可以使用這種方法在 Spring 容器初始化完成後 運行邏輯。因此,我們不關注任何特定的 Bean。而是等待所有 Bean 初始化完成。

為了實現這一點,我們需要創建一個實現 ApplicationListener<ContextRefreshedEvent> 接口的 Bean:

@Component
public class StartupApplicationListenerExample implements 
  ApplicationListener<ContextRefreshedEvent> {

    private static final Logger LOG 
      = Logger.getLogger(StartupApplicationListenerExample.class);

    public static int counter;

    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

我們可以通過使用新引入的 @EventListener 註解來獲得相同的結果:

@Component
public class EventListenerExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(EventListenerExampleBean.class);

    public static int counter;

    @EventListener
    public void onApplicationEvent(ContextRefreshedEvent event) {
        LOG.info("Increment counter");
        counter++;
    }
}

我們希望選擇一個適合我們需求的事件。在本例中,我們選擇了 ContextRefreshedEvent

2.4. @Bean initMethod 屬性

我們可以使用 initMethod 屬性在 Bean 初始化之後運行一個方法。

以下是一個 Bean 的示例:

public class InitMethodExampleBean {

    private static final Logger LOG = Logger.getLogger(InitMethodExampleBean.class);

    @Autowired
    private Environment environment;

    public void init() {
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

請注意,我們尚未實現任何特殊接口,也沒有使用任何特殊註解。

然後,我們可以使用 @Bean 註解來定義 Bean。

@Bean(initMethod="init")
public InitMethodExampleBean initMethodExampleBean() {
    return new InitMethodExampleBean();
}

以下是 Bean 定義在 XML 配置中的外觀:

<bean id="initMethodExampleBean"
  class="com.baeldung.startup.InitMethodExampleBean"
  init-method="init">
</bean>

2.5. 構造函數注入

如果使用構造函數注入來注入字段,我們可以簡單地將邏輯包含在構造函數中:

@Component 
public class LogicInConstructorExampleBean {

    private static final Logger LOG 
      = Logger.getLogger(LogicInConstructorExampleBean.class);

    private final Environment environment;

    @Autowired
    public LogicInConstructorExampleBean(Environment environment) {
        this.environment = environment;
        LOG.info(Arrays.asList(environment.getDefaultProfiles()));
    }
}

2.6. Spring Boot <em>CommandLineRunner</em>

Spring Boot 提供了一個 <em>CommandLineRunner</em> 接口,該接口包含一個回調 <em>run()</em> 方法。該方法可以在 Spring 應用上下文實例化後,在應用程序啓動時被調用。

讓我們來看一個例子:

@Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(CommandLineAppStartupRunner.class);

    public static int counter;

    @Override
    public void run(String...args) throws Exception {
        LOG.info("Increment counter");
        counter++;
    }
}

注意:如文檔 所述,在同一應用程序上下文中可以定義多個 CommandLineRunner Bean,並且可以使用 @Ordered 接口或 @Order 註解對其進行排序。

2.7. Spring Boot <em ApplicationRunner</em>

類似於 <em CommandLineRunner</em>>, Spring Boot 還提供了一個 <em ApplicationRunner</em> 接口,該接口包含一個 <em run()</em> 方法,在應用程序啓動時被調用。但是,與將原始 <em String</em> 類型的參數傳遞到回調方法不同,我們有 `<em ApplicationArguments 類的實例。

<em ApplicationArguments</em> 接口提供了獲取選項值和普通參數值的各種方法。以 -- 開頭的參數是一個選項參數。

以下是一個示例:

@Component
public class AppStartupRunner implements ApplicationRunner {
    private static final Logger LOG =
      LoggerFactory.getLogger(AppStartupRunner.class);

    public static int counter;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        LOG.info("Application started with option names : {}", 
          args.getOptionNames());
        LOG.info("Increment counter");
        counter++;
    }
}

3. 組合機制

為了對我們的 Bean 擁有完全的控制權,我們可以將上述機制組合在一起。

這是執行順序:

  1. 構造函數
  2. 帶有 @PostConstruct 註解的方法
  3. InitializingBean 的 afterPropertiesSet() 方法
  4. 在 XML 中指定的 init-method 方法

讓我們創建一個組合所有機制的 Spring Bean:

@Component
@Scope(value = "prototype")
public class AllStrategiesExampleBean implements InitializingBean {

    private static final Logger LOG 
      = Logger.getLogger(AllStrategiesExampleBean.class);

    public AllStrategiesExampleBean() {
        LOG.info("Constructor");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        LOG.info("InitializingBean");
    }

    @PostConstruct
    public void postConstruct() {
        LOG.info("PostConstruct");
    }

    public void init() {
        LOG.info("init-method");
    }
}

如果嘗試實例化這個 Bean,我們可以看到按照上述指定順序記錄的日誌:

[main] INFO o.b.startup.AllStrategiesExampleBean - Constructor
[main] INFO o.b.startup.AllStrategiesExampleBean - PostConstruct
[main] INFO o.b.startup.AllStrategiesExampleBean - InitializingBean
[main] INFO o.b.startup.AllStrategiesExampleBean - init-method

4. 結論

在本文中,我們展示了多種在 Spring 應用啓動時運行邏輯的方法。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.