詳解Spring中的Profile



前言

由於在項目中使用Maven打包部署的時候,經常由於配置參數過多(比如Nginx服務器的信息、ZooKeeper的信息、數據庫連接、Redis服務器地址等),導致實際現網的配置參數與測試服務器參數混淆,一旦在部署的時候某個參數忘記修改了,那麼就必須重新打包部署,這確實讓人感到非常頭疼。因此就想到使用Spring中的Profile來解決上面描述的問題,並且在此記錄一下其使用的方式,如果有不對的地方,請指正!(感謝)
本文從如下3方面探討Spring的Profile:

  • Spring中的Profile是什麼
  • 爲什麼要使用Profile
  • 如何使用Profile

1.Spring中的Profile 是什麼?

Spring中的Profile功能其實早在Spring 3.1的版本就已經出來,它可以理解爲我們在Spring容器中所定義的Bean的邏輯組名稱,只有當這些Profile被激活的時候,纔會將Profile中所對應的Bean註冊到Spring容器中。舉個更具體的例子,我們以前所定義的Bean,當Spring容器一啓動的時候,就會一股腦的全部加載這些信息完成對Bean的創建;而使用了Profile之後,它會將Bean的定義進行更細粒度的劃分,將這些定義的Bean劃分爲幾個不同的組,當Spring容器加載配置信息的時候,首先查找激活的Profile,然後只會去加載被激活的組中所定義的Bean信息,而不被激活的Profile中所定義的Bean定義信息是不會加載用於創建Bean的。

2.爲什麼要使用Profile

由於我們平時在開發中,通常會出現在開發的時候使用一個開發數據庫,測試的時候使用一個測試的數據庫,而實際部署的時候需要一個數據庫。以前的做法是將這些信息寫在一個配置文件中,當我把代碼部署到測試的環境中,將配置文件改成測試環境;當測試完成,項目需要部署到現網了,又要將配置信息改成現網的,真的好煩。。。而使用了Profile之後,我們就可以分別定義3個配置文件,一個用於開發、一個用戶測試、一個用戶生產,其分別對應於3個Profile。當在實際運行的時候,只需給定一個參數來激活對應的Profile即可,那麼容器就會只加載激活後的配置文件,這樣就可以大大省去我們修改配置信息而帶來的煩惱。

3.配置Spring profile

在介紹完Profile以及爲什麼要使用它之後,下面讓我們以一個例子來演示一下Profile的使用,這裏還是使用傳統的XML的方式來完成Bean的裝配。

3.1 例子需要的Maven依賴

由於只是做一個簡單演示,因此無需引入Spring其他模塊中的內容,只需引入核心的4個模塊+測試模塊即可。

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

3.2 例子代碼

package com.panlingxiao.spring.profile.service;

/**
 * 定義接口,在實際中可能是一個數據源
 * 在開發的時候與實際部署的時候分別使用不同的實現
 */
public interface HelloService {

    public String sayHello();
}

定義生產環境使用的實現類

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配置文件

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

4.激活Profile的其他幾種方式

上面介紹瞭如何使用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容器自動去這些位置讀取然後自動設置,這些位置主要爲如下定義的地方:

  • 作爲SpringMVC中的DispatcherServlet的初始化參數
  • 作爲Web 應用上下文中的初始化參數
  • 作爲JNDI的入口
  • 作爲環境變量
  • 作爲虛擬機的系統參數
  • 使用@AtivceProfile來進行激活

我們在實際的使用過程中,可以定義默認的profile爲開發環境,當實際部署的時候,主需要在實際部署的環境服務器中將spring.profiles.active定義在環境變量中來讓Spring自動讀取當前環境下的配置信息,這樣就可以很好的避免不同環境而頻繁修改配置文件的麻煩。



作者:碼農一枚
鏈接:http://www.jianshu.com/p/948c303b2253
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
發佈了60 篇原創文章 · 獲贊 66 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章