1. 概述
本教程將重點介紹 Spring 中的 Profile 功能。
Profile 是 Spring 框架的核心特性——允許我們映射不同的 Bean 到不同的 Profile,例如 dev、test 和 prod。
我們可以激活不同的 Profile 在不同的環境中,僅啓動我們需要的 Bean。
2. 在 Bean 上使用 @Profile 標註
我們從簡單開始,看看如何將 Bean 映射到特定的 profile。 我們使用 @Profile 註解——這會將 Bean 映射到該 profile;該註解只需指定一個(或多個)profile 的名稱;
考慮一個基本場景:我們有一個 Bean 應該僅在開發期間啓用,但在生產環境中不應部署。
我們使用 dev profile 標註該 Bean,它僅會在開發期間存在於容器中。在生產環境中,dev profile 將不會生效:
@Component
@Profile("dev")
public class DevDatasourceConfig順便説明一下,別名(profile name)也可以使用 NOT 運算符進行前綴,例如 !dev,用於從配置文件中排除它們。
在示例中,組件只有在 dev 配置文件未激活時才會激活:
@Component
@Profile("!dev")
public class DevDatasourceConfig我們還可以使用 ‘&’ 作為 AND 運算符來組合多個用户檔案,例如:
@Component
@Profile("a & b & c")
public class ThreeProfilesComponent在此示例中,ThreeProfilesComponent 僅在 a、b 和 c 三個配置文件都處於激活狀態時才會激活。
同樣,我們可以組合使用 AND 和 NOT 運算符,例如:
@Component
@Profile("!a & !b & !c")
public class NoneOfThreeProfilesComponent噹噹前活動配置文件既不是 a,b,也不是 c 時,NoneOfThreeProfilesComponent 將被激活。
稍後,我們將創建一個小示例應用程序,以演示 @Profile 的工作原理。
3. 在 XML 中聲明配置文件
配置文件也可以通過 XML 進行配置。 <beans> 標籤包含一個 profile 屬性,該屬性接受適用的配置文件的逗號分隔值:
<beans profile="dev">
<bean id="devDatasourceConfig"
class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>4. 設置配置文件
下一步是激活和設置配置文件,以便相應 Bean 被註冊到容器中。
可以通過多種方式進行,我們將會在後續部分進行探討。
4.1. 通過 WebApplicationInitializer 接口進行編程配置
在Web應用程序中,WebApplicationInitializer 可用於通過編程方式配置 ServletContext。
它也是設置活動配置文件(profiles)的非常方便的位置:
@Configuration
public class MyWebApplicationInitializer
implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setInitParameter(
"spring.profiles.active", "dev");
}
}4.2. 通過 ConfigurableEnvironment 編程方式
我們可以直接在環境中設置配置文件:
@Autowired
private ConfigurableEnvironment env;
...
env.setActiveProfiles("someProfile");4.3. Web.xml 中的 Context 參數
類似於使用 context 參數在 Web 應用程序的 <em >web.xml</em > 文件中定義活動配置文件:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/app-config.xml</param-value>
</context-param>
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>4.4. JVM 系統參數
可以通過 JVM 系統參數傳遞配置文件名稱。這些配置文件將在應用程序啓動時激活。
-Dspring.profiles.active=dev4.5. 環境變量
在 Unix 環境中,可以使用 環境變量 激活配置文件:
export spring_profiles_active=dev4.6. Maven 配置文件
Spring 配置文件也可以通過 Maven 配置文件激活,通過指定 spring.profiles.active 配置屬性來實現。
在每個 Maven 配置文件中,可以設置 spring.profiles.active 屬性:
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<spring.profiles.active>dev</spring.profiles.active>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<spring.profiles.active>prod</spring.profiles.active>
</properties>
</profile>
</profiles>它的值將被用於替換@spring.profiles.active@佔位符在application.properties中:
[email protected]@現在我們需要在 pom.xml 中啓用資源過濾:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
...
</build>並添加一個 -P 參數來切換所應用的 Maven 配置文件:
mvn clean package -Pprod此命令將為 prod 配置文件打包應用程序。它還將在應用程序運行時應用 spring.profiles.active 值為 prod 的設置。
4.7. @ActiveProfile 在測試中的使用
測試使得通過使用 @ActiveProfile 註解,輕鬆指定活動配置文件:
@ActiveProfiles("dev")目前,我們已經探討了多種激活 Profile 的方法。現在,讓我們看看哪種優先級最高,如果同時使用多種方法,優先級從高到低是如何確定的:
- Web 應用程序配置中的上下文參數(在 web.xml 中)
- WebApplicationInitializer
- JVM 系統參數
- 環境變量
- Maven Profile
5. 默認配置文件
任何未指定配置文件的 bean 歸屬於 默認 配置文件。
Spring 還提供了在沒有其他配置文件活動時設置默認配置文件的機制——通過使用 spring.profiles.default 屬性。
6. 獲取活動配置文件
Spring 的活動配置文件驅動了 @Profile 註解的行為,用於啓用/禁用 Bean。但是,我們可能也希望通過編程方式訪問活動配置文件的列表。
我們有以下兩種方法可以做到這一點:使用 Environment 或 。
6.1. 使用 環境
我們可以通過注入 環境 對象來訪問當前的活動配置文件。
public class ProfileManager {
@Autowired
private Environment environment;
public void getActiveProfiles() {
for (String profileName : environment.getActiveProfiles()) {
System.out.println("Currently active profile - " + profileName);
}
}
}6.2. 使用 spring.profiles.active
或者,我們可以通過注入屬性 spring.profiles.active 來訪問它們。
@Value("${spring.profiles.active}")
private String activeProfile;在這裏,我們的 activeProfile 變量將包含當前活動 Profile 的名稱,如果存在多個,則以逗號分隔。
然而,我們應該 考慮如果根本沒有活動 Profile 的情況。 憑藉我們上面編寫的代碼,如果沒有活動 Profile,則會阻止應用程序上下文的創建。 這將導致 IllegalArgumentException,因為變量中缺少佔位符進行注入。
為了避免這種情況,我們可以 定義一個默認值:
@Value("${spring.profiles.active:}")
private String activeProfile;現在,如果沒有任何活動配置文件,我們的 activeProfile 將只包含一個空字符串。
如果想要像之前示例一樣訪問這些配置文件列表,我們可以通過分割 activeProfile 變量來實現:
public class ProfileManager {
@Value("${spring.profiles.active:}")
private String activeProfiles;
public String getActiveProfiles() {
for (String profileName : activeProfiles.split(",")) {
System.out.println("Currently active profile - " + profileName);
}
}
}7. 示例:使用配置文件隔離數據源配置
現在我們已經掌握了基本知識,讓我們來看一個實際示例。
7.1. 單個配置文件
考慮一個場景,即我們需要維護開發和生產環境的數據源配置。
讓我們創建一個通用的接口 DatasourceConfig,該接口需要由所有數據源實現類實現:
public interface DatasourceConfig {
public void setup();
}接下來,我們來編寫開發環境的配置:
@Component
@Profile("dev")
public class DevDatasourceConfig implements DatasourceConfig {
@Override
public void setup() {
System.out.println("Setting up datasource for DEV environment. ");
}
}讓我們查看生產環境的配置:
@Component
@Profile("production")
public class ProductionDatasourceConfig implements DatasourceConfig {
@Override
public void setup() {
System.out.println("Setting up datasource for PRODUCTION environment. ");
}
}現在,讓我們創建兩個測試並注入我們的 DatasourceConfig 接口。 根據當前激活的 profile,Spring 將注入 DevDatasourceConfig 或 ProductionDatasourceConfig bean:
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("dev")
@ContextConfiguration(classes = { SpringProfilesConfig.class }, loader = AnnotationConfigContextLoader.class)
public class DevProfileWithAnnotationIntegrationTest {
@Autowired
DatasourceConfig datasourceConfig;
@Test
public void testSpringProfiles() {
Assert.assertTrue(datasourceConfig instanceof DevDatasourceConfig);
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("production")
@ContextConfiguration(classes = { SpringProfilesConfig.class }, loader = AnnotationConfigContextLoader.class)
public class ProductionProfileWithAnnotationIntegrationTest {
@Autowired
DatasourceConfig datasourceConfig;
@Test
public void testSpringProfiles() {
Assert.assertTrue(datasourceConfig instanceof ProductionDatasourceConfig);
}
}
在實際項目中,我們經常需要處理多個環境(或配置文件)。接下來,我們來學習如何在 Spring 中組合環境(或配置文件)。
7.2. 組合多個配置文件
我們將通過示例理解配置文件組合。
假設我們希望僅為 MySQL 數據庫和 Tomcat 平台注入 DatasourceConfig Bean,則可以定義 Bean 為:
@Component
@Profile("tomcat & mysql")
public class TomcatAndMySqlDatasourceConfig implements DatasourceConfig {
@Override
public void setup() {
System.out.println("Setting up MySql datasource for Tomcat environment. ");
}
}
如示例所示,僅當 tomcat 和 mysql 配置文件同時啓用時,才會生成 TomcatAndMySqlDatasourceConfig 實例作為應用程序中的 DatasourceConfig Bean。
接下來,我們創建一個測試來驗證這一點:
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles({"tomcat", "mysql"})
@ContextConfiguration(classes = { SpringProfilesConfig.class }, loader = AnnotationConfigContextLoader.class)
public class TomcatAndMySqlProfileWithAnnotationIntegrationTest {
@Autowired
DatasourceConfig datasourceConfig;
@Test
public void testSpringProfiles() {
Assert.assertTrue(datasourceConfig instanceof TomcatAndMySqlDatasourceConfig);
}
}
在本次測試中,我們使用 @ActiveProfiles 註解同時激活了兩個配置文件。
迄今為止,我們在示例應用程序中定義了以下配置文件:dev、production、tomcat 和 mysql。
現在,如果我們的 Spring 應用程序的活動配置文件既不在上述任何一個配置中,則我們需要注入一個特殊的 DatasourceConfig Bean:
@Component
@Profile("!dev & !production & !mysql & !tomcat")
public class SpecialDatasourceConfig implements DatasourceConfig {
@Override
public void setup() {
System.out.println("Setting up a very special datasource. ");
}
}
我們可以看到,我們使用了(“!a & !b & !c …”)在 <em @Profile</em> 註解中來實現它。 讓我們通過一個簡單的測試來驗證其行為:
@RunWith(SpringJUnit4ClassRunner.class)
@ActiveProfiles("something-very-special")
@ContextConfiguration(classes = { SpringProfilesConfig.class }, loader = AnnotationConfigContextLoader.class)
public class SpecialProfileWithAnnotationIntegrationTest {
@Autowired
DatasourceConfig datasourceConfig;
@Test
public void testSpringProfiles() {
Assert.assertTrue(datasourceConfig instanceof SpecialDatasourceConfig);
}
}
在本次測試中,我們激活了 something-very-special 配置文件,並獲得了預期的 DatasourceConfig Bean。
8. Spring Boot 中的配置文件
Spring Boot 支持迄今為止所有列出的配置文件,並具有一些額外的功能。
8.1. 激活或設置配置文件
初始化參數 <em >spring.profiles.active</em>, 在第 4 節中介紹的,也可以作為 Spring Boot 屬性進行設置,以定義當前激活的配置文件。這是一種 Spring Boot 自動識別的標準屬性:
spring.profiles.active=dev然而,從 Spring Boot 2.4 版本開始,此屬性不能與 spring.config.activate.on-profile 結合使用,因為這可能引發 ConfigDataException(例如,InvalidConfigDataPropertyException 或 InactiveConfigDataAccessException)。
要通過編程方式設置 profile,我們還可以使用 SpringApplication 類:
SpringApplication.setAdditionalProfiles("dev");為了使用 Maven 在 Spring Boot 中設置配置文件,可以在 <em>pom.xm</em><em>l</em> 中指定配置文件的名稱:
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.5.7</version>
<configuration>
<profiles>
<profile>dev</profile>
</profiles>
</configuration>
</plugin>
...
</plugins>並執行針對 Spring Boot 的 Maven 目標:
mvn spring-boot:run8.2. 基於角色的屬性文件
Spring Boot 最重要的基於角色的特性是 基於角色的屬性文件。 這些文件必須採用 application-{profile}.properties 的命名格式。
Spring Boot 會自動加載所有角色的 application.properties 文件,並且僅針對指定角色加載 .properties 文件。
例如,我們可以通過使用名為 application-dev.properties 和 application-production.properties 的兩個文件,為 dev 和 production 角色配置不同的數據源:
在 application-production.properties 文件中,我們可以設置一個 MySql 數據源:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=root
spring.datasource.password=root然後,我們可以為 dev 配置文件在 application-dev.properties 文件中配置相同的屬性,以使用內存中的 H2 數據庫:
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa通過這種方式,我們可以輕鬆地為不同的環境提供不同的配置。
在 Spring Boot 2.4 之前,可以通過特定於文檔的配置文件來激活配置文件是可行的。但現在不行了;在後續版本中,框架會在這種情況下再次拋出 InvalidConfigDataPropertyException 或 InactiveConfigDataAccessException。
8.3. 多文檔文件
為了進一步簡化為不同環境定義屬性,我們可以將同一文件的所有屬性組合在一起,並使用分隔符來指示配置文件。
從 Spring Boot 2.4 版本開始,它擴展了對多文檔文件的支持,用於屬性文件,除了之前支持的 YAML 之外,現在我們可以在同一個 application.properties 中指定 dev 和 production 屬性。
my.prop=used-always-in-all-profiles
#---
spring.config.activate.on-profile=dev
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/db
spring.datasource.username=root
spring.datasource.password=root
#---
spring.config.activate.on-profile=production
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa此文件由 Spring Boot 從上到下讀取。也就是説,如果某個屬性,例如 my.prop,在上述示例中在末尾再次出現,則末尾的值將被視為有效值。
8.4. 配置文件組
Boot 2.4 中新增了配置文件組功能。正如其名稱所示,它允許我們將相似的配置文件組合在一起。
讓我們考慮一個使用場景,即在生產環境中,我們可能擁有多個配置配置文件,例如用於數據庫的 proddb 和用於調度器的 prodquartz,在 production 環境中。
為了通過我們的 application.properties 文件一次性啓用這些配置文件,我們可以指定:
spring.profiles.group.production=proddb,prodquartz因此,激活 production 模式將同時激活 proddb 和 prodquartz。
9. 結論
在本文中,我們討論瞭如何在豆對象上定義配置文件,以及如何啓用這些配置文件在我們的應用程序中。
最後,我們通過一個簡單但具有現實意義的示例來驗證了我們對配置文件的理解。