一、背景
在軟體開發和部署過程中,我們的軟體往往需要在不同的執行環境中執行,例如:開發人員本地開發環境、測試團隊的測試環境、生產模擬環境、正式生產環境,不同的公司可能還會有更多的環境需要對專案配置進行動態切換。專案在這些環境切換的過程中,往往會有一部分配置是所有的環境都相同的,還有一部分是不同環境都不相同的(最典型的就是資料連線配置檔案jdbc.properties),如果我們不做特殊配置,那麼我們就需要根據環境來修改配置檔案,不同環境來回修改和切換,不僅容易出錯,而且很繁瑣,那麼這時候我們在想:有沒有辦法可以能夠讓我們不用修改配置就能釋出到不同的環境中呢?答案顯而易見,那麼本文我們就通過三種方式來解決這個問題。從而把我們的軟體的可移植性提高一個層次。
二、軟體環境
Spring 4.2.6.RELEASE
SpringMvc 4.2.6.RELEASE
Mybatis 3.2.8
Maven 3.3.9
Jdk 1.7
Idea 15.04
首先我們在spring-dao.xml有如下資料來源定義:
<!-- 配置資料來源,資料庫連線池 --> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> <!--資料庫驅動--> <property name="driverClassName" value="${jdbc.driver}"/> <!--資料庫連結地址--> <property name="url" value="${jdbc.url}"/> <!--資料庫使用者名稱--> <property name="username" value="${jdbc.username}"/> <!--資料庫連線密碼--> <property name="password" value="${jdbc.password}"/> <!--連線池初始化大小--> <property name="initialSize" value="${jdbc.initialSize}"/> <!--連線池最小數量--> <property name="minIdle" value="${jdbc.minIdle}" /> <!--連線池最大數量--> <property name="maxActive" value="${jdbc.maxActive}"/> <!--連線池等待超時時間--> <property name="maxWait" value="${jdbc.maxWait}"/> <!--配置間隔多久才進行一次檢測,檢測需要關閉空閒連線,單位是毫秒--> <property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}"/> <property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}"/> <property name="testWhileIdle" value="${jdbc.testWhileIdle}"/> <property name="validationQuery" value="${jdbc.validationQuery}"/> <property name="testOnReturn" value="${jdbc.testOnReturn}"/> <!--開啟PSCache,並且制定每個連線上PSCache的大小--> <property name="poolPreparedStatements" value="${jdbc.poolPreparedStatements}"/> <property name="maxPoolPreparedStatementPerConnectionSize" value="${jdbc.maxPoolPreparedStatementPerConnectionSize}"/> <!--配置監控統計攔截的filters--> <property name="filters" value="${jdbc.filters}"/> </bean>
然後,我們在classpath的config資料夾下定義:config-development.properties、config-test.properties、config-emulation.properties、config-production.properties四個分別對應本地開發環境、測試環境、模擬環境、生產環境的配置檔案,它們中分別指定著不同的配置引數,如資料庫連線url、資料庫使用者名稱、資料庫密碼等.
三、方式一:使用spring的profile機制實現
1.在spring.xml中的資料來源配置之前定義不同的spring的profile
<!-- 開發環境配置 --> <beans profile="dev"> <context:property-placeholder location="classpath:config/config-development.properties"/> </beans> <!-- 測試環境配置 --> <beans profile="test"> <context:property-placeholder location="classpath:config/config-test.properties"/> </beans> <!-- 模擬環境配置 --> <beans profile="emu"> <context:property-placeholder location="classpath:config/config-emulation.properties"/> </beans> <!-- 生產環境配置 --> <beans profile="prod"> <context:property-placeholder location="classpath:config/config-production.properties"/> </beans> <!--其他和環境無關的配置--> <beans> ..... </beans>
然後spring.xml中和環境無關的配置用一個不設定profile的beans標籤包裹。
2.定義預設生效的profile,也就是當我們沒有主動啟用任何profile的情況下,該配置會生效.
1).web.xml中進行如下設定
<!-- 配置spring的預設profile --> <context-param> <param-name>spring.profiles.default</param-name> <param-value>dev</param-value> </context-param>
2).在spring-dao.xml指定預設profile
<beans profile="default"> <context:property-placeholder location="classpath:config/config-development.properties"/> </beans>
3.多環境切換方式
spring為我們提供了大量的啟用profile的方式:程式碼啟用、系統環境變數啟用、JVM引數啟用、Servlet上下文引數激動等。我們一般用的最多的就是使用JVM引數進行啟用,簡單方便。以tomcat為例,我們只需要在tomcat的啟動指令碼中加入以下JVM引數:-Dspring.profiles.active=xxx (其中xxx為我們在spring-dao.xml中定義的profile的名稱:dev、test、emu、prod),在不同的環境中我們指定該引數的值為環境對應的profile名稱即可。很靈活,程式碼也不需要做出任何改變。
4.使用擴充套件
spring的profile還允許我們在java程式碼或者jsp的el表示式中來根據該引數做不同的操作。如:
public void test() { //偵測jvm環境 String env = System.getProperty("spring.profiles.active"); if(env == "dev") { do xxx; } else { do other things; } }
在jsp的el表示式中使用,比如我們只需要在生產環境新增統計流量的程式碼等:
<!-- 生產環境統計、推送程式碼 --> <c:if test="${spring.profiles.active == 'prod' }"> <script> //統計程式碼 .. </script> </c:if>
四、方式二:使用maven的profile機制實現
1.在專案的pom.xml中定義maven profiles
<profiles> <profile> <!-- 本地開發環境 --> <id>dev</id> <properties> <environment>development</environment> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <!-- 測試環境 --> <id>test</id> <properties> <environment>test</environment> </properties> </profile> <profile> <!-- 模擬環境 --> <id>emu</id> <properties> <environment>emulation</environment> </properties> </profile> <profile> <!-- 生產環境 --> <id>prod</id> <properties> <environment>production</environment> </properties> </profile> </profiles>
這裡我們定義了四個環境,分別為:development(開發環境)、test(測試環境)、emulation(模擬環境)、production(生產環境),其中開發環境模式是啟用的(activeByDefault為true),這樣如果我們在不指定profile的情況下預設是開發環境。
2.在spring-dao.xml中進行如下配置:
<context:property-placeholder location="classpath:config/config-${environment}.properties"/>
3.接著在pom.xml中定義resources中要被過濾的資源
<build> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources> </build>
4.不同環境構建或者打包專案
所有的上述步驟昨晚以後,見證奇蹟的時候到了,我們只需要在執行maven命令的時候指定使用不同的profile名稱就可以實現構建或釋出到不同環境需要的war包,如:
mvn clean package -Pprod 即構建出生產環境需要的war包
mvn clean install -Ptest 即構建要釋出到測試環境的war包
五、方式三:使用maven的profile + resources + filter實現
1.首先和第二種方式一樣在pom.xml中進行如下配置各個環境對應的profiles
<profiles> <profile> <id>dev</id> <properties> <environment>development</environment> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>test</id> <properties> <environment>test</environment> </properties> </profile> <profile> <id>emu</id> <properties> <environment>emulation</environment> </properties> </profile> <profile> <id>prod</id> <properties> <environment>production</environment> </properties> </profile> </profiles>
2.接著在pom.xml中定義resources中要被過濾的資源以及過濾時用到的資原始檔
<build> <resources> <resource> <directory>src/main/resources</directory> <excludes> <exclude>config/*</exclude> </excludes> <filtering>true</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>config/config-${environment}.properties</include> </includes> <filtering>false</filtering> </resource> </resources> <filters> <filter>src/main/resources/config/config-${environment}.properties</filter> </filters> </build>
注:上面配置中第一個resource定義了排除config資料夾下的所有檔案,並且對src/main/resources資料夾進行過濾替換佔位符,使用的配置檔案是紅色部分標出的檔案,第二個resource定義了將config資料夾下config-${environment}.properties檔案作為資原始檔,並且不對該檔案做過濾。
3.不同環境構建或者打包專案
我們只需要在執行maven命令的時候指定使用不同的profile名稱就可以實現構建或釋出到不同環境需要的war包,如:
mvn clean package -Pprod 即構建出生產環境需要的war包
mvn clean install -Ptest 即構建要釋出到測試環境的war包
專案github地址:https://github.com/hafizzhang/maven-profile.git
六、總結
三種方式都可以很完美的實現多環境打包部署,但方式一依賴spring,方式二和方式三依賴maven。第二種方式是執行時替換佔位符,可以使用JVM引數來替換配置檔案內容,第三種方式是打包時替換佔位符,不支援執行時通過JVM引數替換配置檔案內容。我個人更傾向於和推薦使用第二種方式,它靈活多變,可擴充套件性高!