因爲在項目中使用Maven打包部署的時候,常常因爲配置參數過多(好比Nginx服務器的信息、ZooKeeper的信息、數據庫鏈接、Redis服務器地址等),致使實際現網的配置參數與測試服務器參數混淆,一旦在部署的時候某個參數忘記修改了,那麼就必須從新打包部署,這確實讓人感到很是頭疼。所以就想到使用Spring中的Profile來解決上面描述的問題,而且在此記錄一下其使用的方式,若是有不對的地方,請指正!(感謝)。
本文從以下3方面探討Spring的Profile:java
Spring中的Profile功能其實早在Spring 3.1的版本就已經出來,它能夠理解爲咱們在Spring容器中所定義的Bean的邏輯組名稱,只有當這些Profile被激活的時候,纔會將Profile中所對應的Bean註冊到Spring容器中。舉個更具體的例子,咱們之前所定義的Bean,當Spring容器一啓動的時候,就會一股腦的所有加載這些信息完成對Bean的建立;而使用了Profile以後,它會將Bean的定義進行更細粒度的劃分,將這些定義的Bean劃分爲幾個不一樣的組,當Spring容器加載配置信息的時候,首先查找激活的Profile,而後只會去加載被激活的組中所定義的Bean信息,而不被激活的Profile中所定義的Bean定義信息是不會加載用於建立Bean的。spring
因爲咱們平時在開發中,一般會出如今開發的時候使用一個開發數據庫,測試的時候使用一個測試的數據庫,而實際部署的時候須要一個數據庫。之前的作法是將這些信息寫在一個配置文件中,當我把代碼部署到測試的環境中,將配置文件改爲測試環境;當測試完成,項目須要部署到現網了,又要將配置信息改爲現網的,真的好煩。。。而使用了Profile以後,咱們就能夠分別定義3個配置文件,一個用於開發、一個用戶測試、一個用戶生產,其分別對應於3個Profile。當在實際運行的時候,只需給定一個參數來激活對應的Profile便可,那麼容器就會只加載激活後的配置文件,這樣就能夠大大省去咱們修改配置信息而帶來的煩惱。shell
在介紹完Profile以及爲何要使用它以後,下面讓咱們以一個例子來演示一下Profile的使用,這裏仍是使用傳統的XML的方式來完成Bean的裝配。數據庫
因爲只是作一個簡單演示,所以無需引入Spring其餘模塊中的內容,只需引入核心的4個模塊+測試模塊便可。express
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!--指定Spring版本,該版本必須大於等於3.1--> <spring.version>4.2.4.RELEASE</spring.version> <!--指定JDK編譯環境--> <java.version>1.7</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> </build>
package com.panlingxiao.spring.profile.service; /** * 定義接口,在實際中多是一個數據源 * 在開發的時候與實際部署的時候分別使用不一樣的實現 */ public interface HelloService { public String sayHello(); }
定義生產環境使用的實現類apache
package com.panlingxiao.spring.profile.service.produce; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.panlingxiao.spring.profile.service.HelloService; /** * 模擬在生產環境下須要使用的類 */ @Component public class ProduceHelloService implements HelloService { //這個值讀取生產環境下的配置注入 @Value("#{config.name}") private String name; public String sayHello() { return String.format("hello,I'm %s,this is a produce environment!", name); } }
定義開發下使用的實現類服務器
package com.panlingxiao.spring.profile.service.dev; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.panlingxiao.spring.profile.service.HelloService; /** * 模擬在開發環境下使用類 */ @Component public class DevHelloService implements HelloService{ //這個值是讀取開發環境下的配置文件注入 @Value("#{config.name}") private String name; public String sayHello() { return String.format("hello,I'm %s,this is a development environment!", name); } }
定義配置Spring配置文件maven
<?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:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd"> <!-- 定義開發的profile --> <beans profile="development"> <!-- 只掃描開發環境下使用的類 --> <context:component-scan base-package="com.panlingxiao.spring.profile.service.dev" /> <!-- 加載開發使用的配置文件 --> <util:properties id="config" location="classpath:dev/config.properties"/> </beans> <!-- 定義生產使用的profile --> <beans profile="produce"> <!-- 只掃描生產環境下使用的類 --> <context:component-scan base-package="com.panlingxiao.spring.profile.service.produce" /> <!-- 加載生產使用的配置文件 --> <util:properties id="config" location="classpath:produce/config.properties"/> </beans> </beans>
開發使用的配置文件,dev/config.properties單元測試
name=Tomcat
生產使用的配置文件,produce/config.properties測試
name=Jetty
編寫測試類
package com.panlingxiao.spring.profile.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.panlingxiao.spring.profile.service.HelloService; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:spring-profile.xml") /* * 使用註冊來完成對profile的激活, * 傳入對應的profile名字便可,能夠傳入produce或者dev */ @ActiveProfiles("produce") public class TestActiveProfile { @Autowired private HelloService hs; @Test public void testProfile() throws Exception { String value = hs.sayHello(); System.out.println(value); } }
激活dev運行結果.png
激活produce運行結果.jpg
上面介紹瞭如何使用Profile以及在單元測試的環境下激活指定的Profile,除了使用@ActiveProfiles註解來激活profile外,Spring還提供了其餘的幾種激活Profile,這些方式在實際的開發中使用的更多。
Spring經過兩個不一樣屬性來決定哪些profile能夠被激活(注意:profile是能夠同時激活多個的),一個屬性是spring.profiles.active和spring.profiles.default。這兩個常量值在Spring的AbstractEnvironment中有定義,查看AbstractEnvironment源碼:
/** * Name of property to set to specify active profiles: {@value}. Value may be comma * delimited. * <p>Note that certain shell environments such as Bash disallow the use of the period * character in variable names. Assuming that Spring's {@link SystemEnvironmentPropertySource} * is in use, this property may be specified as an environment variable as * {@code SPRING_PROFILES_ACTIVE}. * @see ConfigurableEnvironment#setActiveProfiles */ public static final String ACTIVE_PROFILES_PROPERTY_NAME = "spring.profiles.active"; /** * Name of property to set to specify profiles active by default: {@value}. Value may * be comma delimited. * <p>Note that certain shell environments such as Bash disallow the use of the period * character in variable names. Assuming that Spring's {@link SystemEnvironmentPropertySource} * is in use, this property may be specified as an environment variable as * {@code SPRING_PROFILES_DEFAULT}. * @see ConfigurableEnvironment#setDefaultProfiles */ public static final String DEFAULT_PROFILES_PROPERTY_NAME = "spring.profiles.default";
若是當spring.profiles.active屬性被設置時,那麼Spring會優先使用該屬性對應值來激活Profile。當spring.profiles.active沒有被設置時,那麼Spring會根據spring.profiles.default屬性的對應值來進行Profile進行激活。若是上面的兩個屬性都沒有被設置,那麼就不會有任務Profile被激活,只有定義在Profile以外的Bean纔會被建立。咱們發現這兩個屬性值實際上是Spring容器中定義的屬性,而咱們在實際的開發中不多會直接操做Spring容器自己,因此若是要設置這兩個屬性,實際上是須要定義在特殊的位置,讓Spring容器自動去這些位置讀取而後自動設置,這些位置主要爲以下定義的地方:
咱們在實際的使用過程當中,能夠定義默認的profile爲開發環境,當實際部署的時候,主須要在實際部署的環境服務器中將spring.profiles.active定義在環境變量中來讓Spring自動讀取當前環境下的配置信息,這樣就能夠很好的避免不一樣環境而頻繁修改配置文件的麻煩。