知識庫 / Spring / Spring Boot RSS 訂閱

使用 Spring Boot 創建自定義自動配置

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

1. 概述

簡單來説,Spring Boot的自動配置功能可以根據類路徑上存在的依賴自動配置一個Spring應用程序。

這可以加快和簡化開發過程,避免了定義某些在自動配置類中包含的Bean的需求。

在下一部分,我們將學習如何創建自定義的Spring Boot自動配置

2. Maven 依賴

讓我們從依賴項開始:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>

從 Maven Central 可以下載最新版本的 spring-boot-starter-data-jpamysql-connector-java

3. 創建自定義自動配置

為了創建自定義自動配置,我們需要創建一個標註了 @Configuration 的類並進行註冊。

讓我們創建一個自定義配置,用於 MySQL 數據源:

@Configuration
public class MySQLAutoconfiguration {
    //...
}

接下來,我們需要將該類註冊為自動配置候選者。

通過在標準文件 resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中添加該類的名稱來實現。

com.baeldung.autoconfiguration.MySQLAutoconfiguration

為了使我們的自動配置類具有優先權,我們可以添加 @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) 註解。

我們使用帶有 @Conditional 註解的類和 Bean 來設計自動配置,以便我們可以替換自動配置或其中的一部分。

請注意,自動配置只有在未定義自動配置 Bean 的情況下才生效。如果定義了我們的 Bean,它將覆蓋默認的 Bean。

3.1. 類條件

類條件允許我們使用 <em @ConditionalOnClass</em> 註解指定,如果存在指定的類,則包含一個配置 Bean;或者使用 <em @ConditionalOnMissingClass</em> 註解指定,如果類不存在時。

例如,我們可以指定 <em MySQLConfiguration</em> 只在 <em DataSource</em> 類存在時加載,從而推斷應用程序將使用數據庫:

@Configuration
@ConditionalOnClass(DataSource.class)
public class MySQLAutoconfiguration {
    //...
}

3.2. Bean 條件

如果希望僅在指定 Bean 存在或不存在時才包含 Bean,可以使用 <em>@ConditionalOnBean</em><em>@ConditionalOnMissingBean</em> 註解。

為了説明這一點,讓我們將 <em>entityManagerFactory</em> Bean 添加到我們的配置類中。

首先,我們將指定僅在名為 <em>dataSource</em> 的 Bean 存在的情況下才創建此 Bean,並且 <em>entityManagerFactory</em> Bean 尚未定義:

@Bean
@ConditionalOnBean(name = "dataSource")
@ConditionalOnMissingBean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    LocalContainerEntityManagerFactoryBean em
      = new LocalContainerEntityManagerFactoryBean();
    em.setDataSource(dataSource());
    em.setPackagesToScan("com.baeldung.autoconfiguration.example");
    em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
    if (additionalProperties() != null) {
        em.setJpaProperties(additionalProperties());
    }
    return em;
}

讓我們也配置一個 transactionManager Bean,它會在我們已經定義了類型為 JpaTransactionManager 的 Bean 時才加載:

@Bean
@ConditionalOnMissingBean(type = "JpaTransactionManager")
JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManager.setEntityManagerFactory(entityManagerFactory);
    return transactionManager;
}

3.3. 屬性條件

我們使用 @ConditionalOnProperty 註解來 指定配置是否基於 Spring 環境中屬性的存在和值加載。

首先,讓我們添加一個用於確定屬性讀取位置的屬性源文件:

@PropertySource("classpath:mysql.properties")
public class MySQLAutoconfiguration {
    //...
}

我們可以配置主 DataSource Bean,以便在 usemysql 屬性存在時才加載連接到數據庫的連接。

我們可以使用 havingValue 屬性來指定必須匹配的 usemysql 屬性的值。

現在,讓我們定義 dataSource Bean,並設置默認值,如果將 usemysql 屬性設置為 local,則連接到名為 myDb 的本地數據庫。

@Bean
@ConditionalOnProperty(
  name = "usemysql", 
  havingValue = "local")
@ConditionalOnMissingBean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
 
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/myDb?createDatabaseIfNotExist=true");
    dataSource.setUsername("mysqluser");
    dataSource.setPassword("mysqlpass");

    return dataSource;
}

如果我們將 usemysql 屬性設置為 custom,我們將使用自定義屬性值來配置 dataSource Bean,包括數據庫 URL、用户名和密碼:

@Bean(name = "dataSource")
@ConditionalOnProperty(
  name = "usemysql", 
  havingValue = "custom")
@ConditionalOnMissingBean
public DataSource dataSource2() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
        
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl(env.getProperty("mysql.url"));
    dataSource.setUsername(env.getProperty("mysql.user") != null 
      ? env.getProperty("mysql.user") : "");
    dataSource.setPassword(env.getProperty("mysql.pass") != null 
      ? env.getProperty("mysql.pass") : "");
        
    return dataSource;
}

mysql.properties 文件將包含usemysql 屬性:

usemysql=local

一個使用 MySQLAutoconfiguration 的應用程序可能需要覆蓋默認屬性。在這種情況下,只需要為 mysql.urlmysql.usermysql.pass 屬性添加不同的值,以及在 mysql.properties 文件中的 usemysql=custom 行進行修改。

3.4. 資源條件

添加 <em/>@ConditionalOnResource</em/> 註解意味着配置僅在指定的資源存在時才加載。

定義一個名為 <em/>additionalProperties()</em/> 的方法,該方法將返回一個 <em/>Properties</em/> 對象,其中包含用於entityManagerFactory</em/> bean 的 Hibernate 專用屬性,僅當資源文件 <em/>mysql.properties</em/> 存在時才使用:

@ConditionalOnResource(
  resources = "classpath:mysql.properties")
@Conditional(HibernateCondition.class)
Properties additionalProperties() {
    Properties hibernateProperties = new Properties();

    hibernateProperties.setProperty("hibernate.hbm2ddl.auto", 
      env.getProperty("mysql-hibernate.hbm2ddl.auto"));
    hibernateProperties.setProperty("hibernate.dialect", 
      env.getProperty("mysql-hibernate.dialect"));
    hibernateProperties.setProperty("hibernate.show_sql", 
      env.getProperty("mysql-hibernate.show_sql") != null 
      ? env.getProperty("mysql-hibernate.show_sql") : "false");
    return hibernateProperties;
}

我們可以在 mysql.properties文件中添加 Hibernate 相關的屬性:

mysql-hibernate.dialect=org.hibernate.dialect.MySQLDialect
mysql-hibernate.show_sql=true
mysql-hibernate.hbm2ddl.auto=create-drop

3.5. 自定義條件

假設我們不想使用 Spring Boot 中提供的任何條件。

我們可以通過擴展 SpringBootCondition 類並覆蓋 getMatchOutcome() 方法來定義自定義條件。

讓我們為我們的 additionalProperties() 方法創建一個名為 HibernateCondition 的條件,該條件將驗證 classpath 上是否存在 HibernateEntityManager 類:

static class HibernateCondition extends SpringBootCondition {

    private static String[] CLASS_NAMES
      = { "org.hibernate.ejb.HibernateEntityManager", 
          "org.hibernate.jpa.HibernateEntityManager" };

    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, 
      AnnotatedTypeMetadata metadata) {
 
        ConditionMessage.Builder message
          = ConditionMessage.forCondition("Hibernate");
        return Arrays.stream(CLASS_NAMES)
          .filter(className -> ClassUtils.isPresent(className, context.getClassLoader()))
          .map(className -> ConditionOutcome
            .match(message.found("class")
            .items(Style.NORMAL, className)))
          .findAny()
          .orElseGet(() -> ConditionOutcome
            .noMatch(message.didNotFind("class", "classes")
            .items(Style.NORMAL, Arrays.asList(CLASS_NAMES))));
    }
}

然後,我們可以將條件添加到 additionalProperties() 方法中:

@Conditional(HibernateCondition.class)
Properties additionalProperties() {
  //...
}

3.6. 應用條件

我們可以指定配置只能在/不在 Web 上下文中加載。 要實現此目的,可以添加 @ConditionalOnWebApplication@ConditionalOnNotWebApplication 註解。

4. 測試自動配置

讓我們創建一個簡單的示例來測試我們的自動配置。

我們將創建一個名為 MyUser 的實體類,以及使用 Spring Data 創建的 MyUserRepository 接口:

@Entity
public class MyUser {
    @Id
    private String email;

    // standard constructor, getters, setters
}
public interface MyUserRepository 
  extends JpaRepository<MyUser, String> { }

為了啓用自動配置,我們可以使用 @SpringBootApplication@EnableAutoConfiguration 註解之一:

@SpringBootApplication
public class AutoconfigurationApplication {
    public static void main(String[] args) {
        SpringApplication.run(AutoconfigurationApplication.class, args);
    }
}

接下來,我們編寫一個使用 JUnit 框架的測試,用於保存 MyUser 實體:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(
  classes = AutoconfigurationApplication.class)
@EnableJpaRepositories(
  basePackages = { "com.baeldung.autoconfiguration.example" })
public class AutoconfigurationLiveTest {

    @Autowired
    private MyUserRepository userRepository;

    @Test
    public void whenSaveUser_thenOk() {
        MyUser user = new MyUser("[email protected]");
        userRepository.save(user);
    }
}

由於我們未定義 DataSource 配置,應用程序將使用我們創建的自動配置連接到名為 myDbMySQL 數據庫。

連接字符串包含 createDatabaseIfNotExist=true 屬性,因此數據庫無需事先存在。但是,mysqluser 用户,或通過 mysql.user 屬性(如果存在)指定的用户需要創建。

我們可以查看應用程序日誌以確認我們正在使用 MySQL 數據源:

web - 2017-04-12 00:01:33,956 [main] INFO  o.s.j.d.DriverManagerDataSource - Loaded JDBC driver: com.mysql.cj.jdbc.Driver

5. 取消自動配置類

假設我們想排除自動配置的加載

我們可以通過在配置類上添加 @EnableAutoConfiguration 註解,並使用 excludeexcludeName 屬性來完成:

@Configuration
@EnableAutoConfiguration(
  exclude={MySQLAutoconfiguration.class})
public class AutoconfigurationApplication {
    //...
}

我們還可以設置 spring.autoconfigure.exclude 屬性:

spring.autoconfigure.exclude=com.baeldung.autoconfiguration.MySQLAutoconfiguration

6. 結論

在本文中,我們展示瞭如何創建自定義 Spring Boot 自定義配置。

可以使用 autoconfiguration 配置文件運行 JUnit 測試:mvn clean install -Pautoconfiguration

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

發佈 評論

Some HTML is okay.