無論項目大小,數據庫、開發環境、測試環境....這些信息,肯定不能在程序中硬編碼。
硬編碼有百壞而無一利,每次信息變動都要重新編譯項目,不能分離運維和開發。
而且配置散落在項目的程序中,無法做到准確集中管理,拖慢項目進度。
我想這也是配置文件出現的原因,配置文件比較主流的格式 properties(鍵值對形式)、xml(對象,復雜數據結構,只有你想不到沒有xml 表達不了的) 等。
本文已比較常見、簡單的 properties 格式的配置文件為例,來看看讀取配置文件幾種不同的姿勢,關注其中的實現和使用,設計模式另表。
1. Spring 和 Apache Commons Configuration
如果項目中沒什么特殊的個性化讀取配置文件需求,可以使用 Spring 管理配置文件信息,然后注入到需要的地方。
配置文件中需要添加(PS :多配置文件,添加 ignore-unresolvable 參數)。
<context:property-placeholder location="classpath:db-info.properties" ignore-unresolvable="true"/> <context:property-placeholder location="classpath:web.properties" ignore-unresolvable="true"/>
然后在后端服務器需要的地方:
@Value("${uploadpath}") protected String uploadPath;
動態讀入可以使用 Spring 提供了默認的配置文件讀取實現類 org.springframework.core.io.DefaultResourceLoader。
當然你也可以實現 org.springframework.core.io.ResourceLoader 接口自定義配置文件載入實現類。
org.springframework.core.io.DefaultResourceLoader 核心方法 getResource:
public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); Iterator ex = this.protocolResolvers.iterator(); Resource resource; do { if(!ex.hasNext()) { if(location.startsWith("/")) { return this.getResourceByPath(location); } if(location.startsWith("classpath:")) { return new ClassPathResource(location.substring("classpath:".length()), this.getClassLoader()); } try { URL ex1 = new URL(location); return new UrlResource(ex1); } catch (MalformedURLException var5) { return this.getResourceByPath(location); } } ProtocolResolver protocolResolver = (ProtocolResolver)ex.next(); resource = protocolResolver.resolve(location, this); } while(resource == null); return resource; }
可以看出 Spring 能支持入參路徑的很多方式,包括已 " /"、"classpath" 開頭。
如果你想在項目中使用 Spring 提供的默認配置文件載入實現,可以這樣書寫你的代碼。
ResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource("log4j.properties"); Properties props = new Properties(); props.load(resource.getInputStream());
當然你也可以引入 Apache Commons Configuration jar 內部設計相當考究。
整個 jar 不超過 400K,如果時間充裕,你也可以反編譯看看源碼。
使用方式也特別簡潔,兩行代碼就 OK:
PropertiesConfiguration configuration = new PropertiesConfiguration("log4j.properties"); configuration.getString("log4j.appender.file");
Apache Commons Configuration 默認載入配置文件核心實現類 org.apache.commons.configuration.AbstractFileConfiguration 載入方法:
public void load(String fileName) throws ConfigurationException { try { URL e = ConfigurationUtils.locate(this.fileSystem, this.basePath, fileName); if(e == null) { throw new ConfigurationException("Cannot locate configuration source " + fileName); } else { this.load(e); } } catch (ConfigurationException var3) { throw var3; } catch (Exception var4) { throw new ConfigurationException("Unable to load the configuration file " + fileName, var4); } }
2. JDK 經典手寫
如果你項目對讀取配置文件沒有太多個性化的需求,如果你有足夠時間,如果你嫌棄第三方 Jar 占據你 lib 目錄的一席之地,還有如果你熱愛編程。
仔細一點,你會發現,這些開源框架底層都是已 java.net.URL 載入配置文件。
在載入配置文件的過程中應項目需求采用了恰當的設計模式,使能夠支持一些對配置文件的特定操作。
載入文件后,實例化為 java.util.Properties 對象,進行配置文件獲取。
那就完全可以擼段純 JDK 的寫法,作為工具類放入項目中,編譯后不超過 5K,核心的幾句代碼如下:
URL resource = Thread.currentThread().getContextClassLoader().getResource("log4j.properties"); Properties properties = new Properties(); properties.load(resource.openStream()); properties.getProperty(key);
因為 java.util.Properties 的 load 進行了方法的重載,你也可以不用 URL 方式讀取配置文件,也可以這樣寫:
String url = this.getClass().getClassLoader().getResource("").getPath().replaceAll("%20", " "); String path = url.substring(0, url.indexOf("classes")) + filePath; //該 path 為你配置文件的路徑 InputStream inputStream = new FileInputStream(path); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); prop.load(bufferedReader);prop.getProperty(key);
上述代碼都為實例核心的幾句代碼,其中的判空和異常都沒有進行處理,僅作為參考。
最后貼上自己封裝的配置文件載入類,不使用任何第三方 jar,有需要的拿走放入項目即可用。

package com.rambo.sme.util; import java.io.IOException; import java.net.URL; import java.util.NoSuchElementException; import java.util.Properties; /** * Properties文件載入工具類. 可載入多個properties文件 * 同一屬性在最后載入的文件中的值將會覆蓋之前的值,以System的Property優先. */ public class PropertiesLoader { private final Properties properties; public PropertiesLoader(String... resourcesPaths) { properties = loadProperties(resourcesPaths); } public Properties getProperties() { return properties; } /** * 取出String類型的Property,但以System的Property優先,如果都為Null則拋出異常. */ public String getProperty(String key) { String value = getValue(key); if (value == null) { throw new NoSuchElementException(); } return value; } /** * 取出String類型的Property,但以System的Property優先.如果都為Null則返回Default值. */ public String getProperty(String key, String defaultValue) { String value = getValue(key); return value != null ? value : defaultValue; } /** * 取出Integer類型的Property,但以System的Property優先.如果都為Null或內容錯誤則拋出異常. */ public Integer getInteger(String key) { String value = getValue(key); if (value == null) { throw new NoSuchElementException(); } return Integer.valueOf(value); } /** * 取出Integer類型的Property,但以System的Property優先.如果都為Null則返回Default值,如果內容錯誤則拋出異常 */ public Integer getInteger(String key, Integer defaultValue) { String value = getValue(key); return value != null ? Integer.valueOf(value) : defaultValue; } /** * 取出Double類型的Property,但以System的Property優先.如果都為Null或內容錯誤則拋出異常. */ public Double getDouble(String key) { String value = getValue(key); if (value == null) { throw new NoSuchElementException(); } return Double.valueOf(value); } /** * 取出Double類型的Property,但以System的Property優先.如果都為Null則返回Default值,如果內容錯誤則拋出異常 */ public Double getDouble(String key, Integer defaultValue) { String value = getValue(key); return value != null ? Double.valueOf(value) : new Double(defaultValue); } /** * 取出Boolean類型的Property,但以System的Property優先.如果都為Null拋出異常,如果內容不是true/false則返回false. */ public Boolean getBoolean(String key) { String value = getValue(key); if (value == null) { throw new NoSuchElementException(); } return Boolean.valueOf(value); } /** * 取出Boolean類型的Property,但以System的Property優先.如果都為Null則返回Default值,如果內容不為true/false則返回false. */ public Boolean getBoolean(String key, boolean defaultValue) { String value = getValue(key); return value != null ? Boolean.valueOf(value) : defaultValue; } /** * 取出Property,但以System的Property優先,取不到返回空字符串. */ private String getValue(String key) { String systemProperty = System.getProperty(key); if (systemProperty != null) { return systemProperty; } if (properties.containsKey(key)) { return properties.getProperty(key); } return ""; } /** * 載入多個文件, 文件路徑使用Spring Resource格式. */ private Properties loadProperties(String... resourcesPaths) { Properties props = new Properties(); for (String location : resourcesPaths) { try { URL url = Thread.currentThread().getContextClassLoader().getResource(location); if(url != null){ props.load(url.openStream()); } } catch (IOException ex) { System.out.println("Could not load properties from path:" + location + ", " + ex.getMessage()); } } return props; } }
使用方式也很簡單,支持多路徑讀入,如果存在相同 Key 后面的覆蓋前面的:
PropertiesLoader propertiesLoader = new PropertiesLoader("log4j.properties"); System.out.println(propertiesLoader.getProperty("log4j.appender.file"));