1. 程式人生 > >補習系列(22)-全面解讀 Spring Profile 的用法

補習系列(22)-全面解讀 Spring Profile 的用法

目錄

  • 一、簡介
  • 二、 區分Bean物件
  • 三、 設定Profile
    • 3.1 WebApplicationInitializer介面
    • 3.2 通過 web.xml定義
    • 3.3 JVM啟動引數
    • 3.4 環境變數
    • 3.5 application.properties
    • 3.6 Maven Profile
    • 3.7 使用 @ActiveProfiles
    • 3.8 使用 ConfigurableEnvironment
    • 3.9 SpringApplication.setAdditionalProfiles
  • 四、 優先順序
  • 五、 案例
  • 參考文件

一、簡介

Profile的意思是配置,對於應用程式來說,不同的環境需要不同的配置。
比如:

  • 開發環境,應用需要連線一個可供除錯的資料庫單機程序
  • 生產環境,應用需要使用正式釋出的資料庫,通常是高可用的叢集
  • 測試環境,應用只需要使用記憶體式的模擬資料庫

Spring框架提供了多profile的管理功能,我們可以使用profile功能來區分不同環境的配置。

二、 區分Bean物件

首先,我們先看看如何基於Profile來定義一個Bean。

通過@Profile註解可以為一個Bean賦予對應的profile名稱,如下:

@Component
@Profile("dev")
public class DevDatasourceConfig

上面的DevDatasourceConfig被定義為 profile=dev,於是該Bean只會在dev(開發環境)模式下被啟用。
如果需要定義為非dev環境,可以使用這樣的形式:

@Component
@Profile("!dev")
public class DevDatasourceConfig

XML風格配置
上面的例子也可以使用XML配置檔案達到同樣的目的,如下:

<beans profile="dev">
    <bean id="devDatasourceConfig"
      class="org.baeldung.profiles.DevDatasourceConfig" />
</beans>

讀取Profile
通過ConfigurableEnvironment這個Bean 可以獲得當前的Profile,如下:

public class ProfileManager {
    @Autowired
    Environment environment;
 
    public void getActiveProfiles() {
        for (final String profileName : environment.getActiveProfiles()) {
            System.out.println("Currently active profile - " + profileName);
        }   
    }
}

三、 設定Profile

接下來,為了讓容器"僅僅註冊那些所需要的Bean",我們需要通過一些手段來設定當前的profile。

有很多方法可以達到這個目的,下面一一介紹。

3.1 WebApplicationInitializer介面

在Web應用程式中,通過WebApplicationInitializer可以對當前的ServletContext進行配置。

如下,通過注入spring.profiles.active變數可以為Spring上下文指定當前的 profile:

@Configuration
public class MyWebApplicationInitializer 
  implements WebApplicationInitializer {
 
    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
  
        servletContext.setInitParameter(
          "spring.profiles.active", "dev");
    }
}

3.2 通過 web.xml定義

與上面的方法類似,在web.xml中通過context-param元素也可以設定profile。

但前提是當前應用程式使用了xml的配置檔案風格,如下:

<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>

3.3 JVM啟動引數

通過Java程式啟動引數同樣可以對profile進行設定,如下:

java -jar application.jar -Dspring.profiles.active=dev

spring-boot-maven-plugin外掛也支援設定profile,其原理也是通過啟動引數實現,可以參考這裡

https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/run-profiles.html

3.4 環境變數

在Unix/Linux環境中,可以通過環境變數注入profile的值:

export spring_profiles_active=dev
java -jar application.jar 

3.5 application.properties

可以在application.properties配置檔案中指定spring.profiles.active屬性:

spring.profiles.active=dev

SpringBoot預設會載入並讀取該配置,當發現為profile=dev時,會同時關聯載入application-dev.properties這個配置。
這種方式非常簡單,可以實現對不同環境採用單獨的配置檔案進行隔離。

3.6 Maven Profile

Maven本身也提供了Profile的功能,可以通過Maven的Profile配置來指定Spring的Profile。

這種做法稍微有點複雜,需要先在pom.xml中設定不同的 maven profile,如下:

<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>

這裡,分別聲明瞭dev和prod兩個profile,每個profile都包含了一個spring.profiles.active屬性,這個屬性用來注入到 Spring中的profile入參。
在SpringBoot的配置檔案application.properties中,需要替換為這個maven傳入的property:

## 使用Maven的屬性進行替換
[email protected]@

接下來,需要讓Maven在打包時能將application.properties進行過濾處理,同時替換掉變數,需編輯pom.xml如下:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

這裡定義了filtering=true,因此Resource打包外掛會對配置檔案執行過濾。

如果你的專案pom定義繼承自 spring-boot-starter-parent,那麼可以不需要配置這個filter

最後,在maven打包時指定引數如下:

mvn clean package -Pprod

3.7 使用 @ActiveProfiles

@ActiveProfile 是用於單元測試場景的註解,可以為測試程式碼指定一個隔離的profile,如下:

@ActiveProfiles("test")
public void ApiTest{
  ...
}

3.8 使用 ConfigurableEnvironment

ConfigurableEnvironment 這個Bean封裝了當前環境的配置資訊,你可以在啟動應用前進行設定操作:

SpringApplication application = new SpringApplication(MyApplication.class);

//設定environment中的profiler
ConfigurableEnvironment environment = new StandardEnvironment();
environment.setActiveProfiles("dev","join_dev");

application.setEnvironment(environment);
application.run(args)

3.9 SpringApplication.setAdditionalProfiles

SpringApplication這個類還提供了setAdditionalProfiles方法,用來讓我們實現"附加"式的profile。
這些profile會同時被啟用,而不是替換原來的active profile,如下:

SpringApplication application = new SpringApplication(MyApplication.class);
application.setAdditionalProfiles("new_dev");

這種方式可以實現無條件的啟用profile,優先順序是最高的。
當然,還可以通過設定spring.profiles.include來達到同樣的目的。

四、 優先順序

至此,我們已經提供了很多種方法來設定 Spring應用的profile,當它們同時存在時則會根據一定優先順序來抉擇,參考如下:

  1. SpringApplication.setAdditionalProfiles
  2. ConfigurableEnvironment、@ActiveProfiles
  3. Web.xml的 context-param
  4. WebApplicationInitializer
  5. JVM 啟動引數
  6. 環境變數
  7. Maven profile、application.properties

從上至下,優先順序從高到低排列。
其中,Maven profile與配置檔案的方式相同,環境變數以及JVM啟動引數會覆蓋配置檔案的內容。
1和2則屬於程序內的控制邏輯,優先順序更高。
如果在啟動SpringBoot應用前對當前ConfigurableEnvironment物件注入了profile,則會優先使用這個引數, ActiveProfiles用於測試環境,其原理與此類似。
SpringApplication.setAdditionalProfiles則是無論如何都會附加的profile,優先順序最高。

五、 案例

最後,我們在SpringBoot中演示一個使用Profile的例子。

一般,在開發環境和生產環境中的資料來源配置是不同的,藉助Profile我們可以定義出不同環境的資料來源Bean。
首先我們先建立一個介面:

public interface DatasourceConfig {
    public void setup();
}

對於開發環境,DatasourceConfig實現如下:

@Component
@Profile("dev")
public class DevDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
        System.out.println("Setting up datasource for DEV environment. ");
    }
}

同樣,為生產環境也實現一個DatasourceConfig:

@Component
@Profile("production")
public class ProductionDatasourceConfig implements DatasourceConfig {
    @Override
    public void setup() {
       System.out.println("Setting up datasource for PRODUCTION environment. ");
    }
}

接下來,我們宣告一個Bean,對資料來源執行初始化方法:

@Component
public class SpringProfilesTest {
    @Autowired
    DatasourceConfig datasourceConfig;
 
    @PostConstruct
    public void setupDatasource() {
        datasourceConfig.setup();
    }
}

之後,在application.properties的配置為:

spring.profiles.active=dev

啟動SpringBoot 應用,發現輸出如下:

Setting up datasource for DEV environment.

此時說明dev的profile被啟用了!

參考文件

https://www.baeldung.com/spring-profiles
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-profiles.html
http://dolszewski.com/spring/spring-boot-properties-per-maven-profile/
https://www.concretepage.com/spring-5/activeprofiles-example-spring-test
https://docs.spring.io/spring-boot/docs/current/maven-plugin/examples/run-profiles.html

點選檢視美碼師的 SpringBoot 補習