Spring入門(七):Spring Profile使用講解

  • 2019 年 10 月 3 日
  • 筆記

1. 使用場景

在日常的開發工作中,我們經常需要將程式部署到不同的環境,比如Dev開發環境,QA測試環境,Prod生產環境,這些環境下的一些配置肯定是不一樣的,比如資料庫配置,Redis配置,RabbitMQ配置。

如果每次切換髮布環境,都需要修改配置重新構建的話,那對程式設計師來說將是噩夢,針對這種場景,Spring提供了@Profile註解來實現按照不同的環境裝配不同的bean,進而實現程式只需構建一次,但可以部署到多個環境。

2. 配置profile bean

為了更好的理解,我們通過具體的程式碼示例來理解下Spring profile的使用方法,這裡我們以資料庫配置為例。

說明:本篇部落格的重點是講解@Profile註解的使用,資料庫的操作只是輔助理解@Profile,因此不會講解的太詳細,不過後續會單獨寫部落格講解

假設我們有3套環境(Dev,QA,Prod),這3套環境的資料庫都使用的是mysql,但是其地址,用戶名,密碼都不一樣,那麼在Java配置中,該如何聲明這些bean呢?

2.1 Java配置中配置profile bean

首先需要了解的是,@Profile註解是從Spring 3.1版本中開始引入的,並且在這個版本中,@Profile註解只能在類級別上使用。

因此我們可以按照環境分別創建資料庫配置,如下所示:

Dev環境下的資料庫配置:

package chapter03.profile;    import org.apache.commons.dbcp2.BasicDataSource;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;  import org.springframework.context.annotation.Profile;    import javax.sql.DataSource;    @Configuration  @Profile("dev")  public class DevDataSourceConfig {      @Bean      public DataSource devDataSource() {          System.out.println("This is dev DataSource");            BasicDataSource basicDataSource = new BasicDataSource();          basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");          basicDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_action_db");          basicDataSource.setUsername("dev");          basicDataSource.setPassword("dev");            return basicDataSource;      }  }

使用上述程式碼需要在pom.xml中添加如下依賴:

<dependency>      <groupId>org.apache.commons</groupId>      <artifactId>commons-dbcp2</artifactId>      <version>2.7.0</version>  </dependency>

注意事項:如果類級別上使用了@Profile("dev"),那麼該類中的所有bean都會在profile為dev時創建。

QA環境下的資料庫配置:

package chapter03.profile;    import org.apache.commons.dbcp2.BasicDataSource;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;  import org.springframework.context.annotation.Profile;    import javax.sql.DataSource;    @Configuration  @Profile("qa")  public class QADataSourceConfig {      @Bean      public DataSource qaDataSource() {          System.out.println("This is qa DataSource");            BasicDataSource basicDataSource = new BasicDataSource();          basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");          basicDataSource.setUrl("jdbc:mysql://localhost:3307/mybatis_action_db");          basicDataSource.setUsername("qa");          basicDataSource.setPassword("qa");            return basicDataSource;      }  }

Prod環境下的資料庫配置:

package chapter03.profile;    import org.apache.commons.dbcp2.BasicDataSource;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;  import org.springframework.context.annotation.Profile;    import javax.sql.DataSource;    @Configuration  @Profile("prod")  public class ProdDataSourceConfig {      @Bean      public DataSource prodDataSource() {          System.out.println("This is prod DataSource");            BasicDataSource basicDataSource = new BasicDataSource();          basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");          basicDataSource.setUrl("jdbc:mysql://localhost:3308/mybatis_action_db");          basicDataSource.setUsername("prod");          basicDataSource.setPassword("prod");            return basicDataSource;      }  }

不過從Spring 3.2開始,@Profile註解可以與@Bean註解一起在方法級別上使用。

這也就使得我們可以將剛剛的3個配置類合併成1個配置類(推薦該方式),如下所示:

package chapter03.profile;    import org.apache.commons.dbcp2.BasicDataSource;  import org.springframework.context.annotation.Bean;  import org.springframework.context.annotation.Configuration;  import org.springframework.context.annotation.Profile;    import javax.sql.DataSource;    @Configuration  public class DataSourceConfig {      @Bean      @Profile("dev")      public DataSource devDataSource() {          System.out.println("This is dev DataSource");            BasicDataSource basicDataSource = new BasicDataSource();          basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");          basicDataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_action_db");          basicDataSource.setUsername("dev");          basicDataSource.setPassword("dev");            return basicDataSource;      }        @Bean      @Profile("qa")      public DataSource qaDataSource() {          System.out.println("This is qa DataSource");            BasicDataSource basicDataSource = new BasicDataSource();          basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");          basicDataSource.setUrl("jdbc:mysql://localhost:3307/mybatis_action_db");          basicDataSource.setUsername("qa");          basicDataSource.setPassword("qa");            return basicDataSource;      }        @Bean      @Profile("prod")      public DataSource prodDataSource() {          System.out.println("This is prod DataSource");            BasicDataSource basicDataSource = new BasicDataSource();          basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");          basicDataSource.setUrl("jdbc:mysql://localhost:3308/mybatis_action_db");          basicDataSource.setUsername("prod");          basicDataSource.setPassword("prod");            return basicDataSource;      }  }

注意事項:沒有指定profile的bean始終都會創建,與激活哪個profile無關。

2.2 xml中配置profile bean

我們也可以通過<beans>元素的profile屬性,在xml中配置profile bean,如下所示:

<?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xmlns:p="http://www.springframework.org/schema/p"         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"         profile="dev">      <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"            p:driverClassName="com.mysql.jdbc.Driver"            p:url="jdbc:mysql://localhost:3306/mybatis_action_db"            p:username="dev"            p:password="dev"/>  </beans>

可以參考該配置,分別創建qa和prod環境的profile xml文件。

不過還是推薦使用嵌套的<beans>元素,在一個xml文件中配置好3個環境的數據源,程式碼如下所示:

<?xml version="1.0" encoding="UTF-8"?>  <beans xmlns="http://www.springframework.org/schema/beans"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xmlns:p="http://www.springframework.org/schema/p"         xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">      <beans profile="dev">          <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"                p:driverClassName="com.mysql.jdbc.Driver"                p:url="jdbc:mysql://localhost:3306/mybatis_action_db"                p:username="dev"                p:password="dev"/>      </beans>      <beans profile="qa">          <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"                p:driverClassName="com.mysql.jdbc.Driver"                p:url="jdbc:mysql://localhost:3307/mybatis_action_db"                p:username="qa"                p:password="qa"/>      </beans>      <beans profile="prod">          <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"                p:driverClassName="com.mysql.jdbc.Driver"                p:url="jdbc:mysql://localhost:3308/mybatis_action_db"                p:username="prod"                p:password="prod"/>      </beans>  </beans>

3. 激活profile

截止目前,我們按照環境的維度創建了3個bean,但實際運行時,只會創建1個bean,具體創建哪個bean取決於處於激活狀態的是哪個profile。

那麼,我們該如何激活某個profile呢?

Spring在確定激活哪個profile時,需要依賴2個屬性:

  1. spring.profiles.active
  2. spring.profiles.default

spring.profiles.active的優先順序比spring.profiles.default高,即如果沒有配置spring.profiles.active,就使用spring.profiles.default配置的值,如果配置了spring.profiles.active,就不會再使用spring.profiles.default配置的值。

如果兩者都沒有配置,就只會創建那些沒有定義profile的bean。

Web應用中,在web.xml中設置spring.profiles.active的程式碼如下所示:

<context-param>      <param-name>spring.profiles.active</param-name>      <param-value>dev</param-value>  </context-param>

也可以使用程式碼方式激活:

AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();    context.getEnvironment().setActiveProfiles("dev");

4. 單元測試

新建Main類,在其main()方法中添加如下測試程式碼:

package chapter03.profile;    import org.springframework.context.annotation.AnnotationConfigApplicationContext;    public class Main {      public static void main(String[] args) {          AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();            context.getEnvironment().setActiveProfiles("dev");          context.register(DataSourceConfig.class);          context.refresh();            context.close();      }  }

輸出結果如下所示:

This is dev DataSource

如果將程式碼修改為context.getEnvironment().setActiveProfiles("qa");,輸出結果為:

This is qa DataSource

如果將程式碼修改為context.getEnvironment().setActiveProfiles("prod");,輸出結果為:

This is prod DataSource

5. 源碼及參考

源碼地址:https://github.com/zwwhnly/spring-action.git,歡迎下載。

汪雲飛《Java EE開發的顛覆者:Spring Boot實戰》

Craig Walls 《Spring實戰(第4版)》

6. 最後

歡迎掃碼關注微信公眾號:「申城異鄉人」,定期分享Java技術乾貨,讓我們一起進步。