web項目部署中存在的配置文件問題:java
web項目以war包的形式,部署在tomcat中,同時項目須要訪問一些其餘的東東,例如訪問數據庫,調用別的項目的API。在開發中,這些須要訪問的外部地址一般以配置文件的形式存在於項目中。以下圖:web
在更換項目的部署環境後,或者調用的外部應用地址發生變化,就須要更改配置文件。spring
最直接的問題:數據庫
發佈了一個測試包,放在測試環境上,測試經過以後,要將測試包發佈到生產環境中,此時測試環境與生產環境的各類配置的地址是不一樣的,解決方案有兩種apache
(1)將配置文件修改成生產環境的配置,從新打包。tomcat
(2)將測試環境的包,升級到生產環境後,修改配置文件爲生產環境的配置文件。app
無論是哪一種方式都是存在風險的。對於(1)來講升級到生產環境的包並非測試經過的包;對於(2)來講增長了人工成本和出錯機率。spring-boot
解決方案:測試
若是配置文件中,須要變化的地址使用變量的形式,項目在被部署到不一樣的環境中後,讀取各自環境中的變量,是否是就能夠解決這個問題了呢。ui
步驟:
(1)在環境變量中增長一個參數配置,tomcat啓動後或者項目啓動後能夠加載改參數
(2)將項目中的配置文件修改爲已經定義的變量
(3)項目的代碼部分在加載配置文件的時候,能夠自動替換掉變量
具體實現:
(1)修改tomcat的Catalina.sh文件
在cygwin=false上面增長JAVA_OPTS="-DbaseDomain=xxxxxx"
設置了變量以後,在java的項目中可使用System.getProperty("baseDomain")來獲取。
(2)修改項目的配置文件,須要修改的內容使用${baseDomain}替換。
(3)項目中解析${baseDomain}
配置完成以後,便可初步實現build once,run any enviroment了。
--------------
附上解析配置文件中${baseDomain}的代碼
import java.util.Properties; import org.apache.log4j.helpers.LogLog; public class OptionConverterUtil { static String DELIM_START = "${"; static char DELIM_STOP = '}'; static int DELIM_START_LEN = 2; static int DELIM_STOP_LEN = 1; public static String substVars(String val, Properties props) throws IllegalArgumentException { StringBuffer sbuf = new StringBuffer(); int i = 0; int j, k; while(true) { j=val.indexOf(DELIM_START, i); if(j == -1) { // no more variables if(i==0) { // this is a simple string return val; } else { // add the tail string which contails no variables and return the result. sbuf.append(val.substring(i, val.length())); return sbuf.toString(); } } else { sbuf.append(val.substring(i, j)); k = val.indexOf(DELIM_STOP, j); if(k == -1) { throw new IllegalArgumentException('"'+val+ "\" has no closing brace. Opening brace at position " + j + '.'); } else { j += DELIM_START_LEN; String key = val.substring(j, k); // first try in System properties String replacement = getSystemProperty(key, null); // then try props parameter if(replacement == null && props != null) { replacement = props.getProperty(key); } if(replacement != null) { // Do variable substitution on the replacement string // such that we can solve "Hello ${x2}" as "Hello p1" // the where the properties are // x1=p1 // x2=${x1} String recursiveReplacement = substVars(replacement, props); sbuf.append(recursiveReplacement); } i = k + DELIM_STOP_LEN; } } } } public static String getSystemProperty(String key, String def) { try { return System.getProperty(key, def); } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx LogLog.debug("Was not allowed to read system property \""+key+"\"."); return def; } } }
在第三步配置文件參數解析操做過程當中參考了log日誌的作法,解析方法也是從log4j的源代碼中copy出來的。
---------------
看了一下spring-boot,若是使用spring-boot,則不須要上述設置,直接配置不一樣的配置文件。spring-boot會根據不一樣的環境加載不一樣的配置文件,原理感受和上述相似。