原由:java
在作項目的過程當中,咱們通常都喜歡把經常使用的配置參數寫在properties文件而後經過Properties類來加載並讀取,爲了使用方便通常會定義多個靜態變量每一個靜態變量對應properties文件的value值。這種方式適用於properties文件比較少的狀況下,若是配置文件比較多則該操做會很繁瑣並且後續修改維護起來比較麻煩。最近在重構一個N年前的系統,發現裏面有10幾個這樣的配置文件,實在寫得煩了所以在想是否是能夠作到每新增一個配置文件只須要把對應bean文件寫好,程序就能夠自動把配置文件的值set到對應的java bean?因而就有了這篇文章。web
思路:spring
1.掃描properties文件jsp
爲了實現properties文件能自動注入對應的java bean文件那麼第一步固然必需要掃描package目錄下的全部.properties文件。實現方法至少有2種,1是經過JDK的getResource("/").getPath()來實現。2.是經過spring 的PathMatchingResourcePatternResolver來實現。在這裏推薦用第2種,由於第1種在不一樣的jsp容器中有可能獲取不到真實的class文件目錄(weblogic)。code
具體的實現代碼爲:對象
//項目根目錄 public static String rootPath ; //須要掃描的properties配置文件存放路徑 public static String propertiesPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX+"conf/**/*.properties" ; //保存properties對象key=conf/**(路徑) public static Map<String,Properties> propMap = new HashMap<String,Properties>() ; /** * 初始化PropMap */ public static void initPropMap(){ ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); try { //獲取根路徑 if(rootPath==null)rootPath= resourcePatternResolver.getResource("/").getURI().getPath() ; //獲取全部的properties文件 Resource[] resources = resourcePatternResolver.getResources(propertiesPath) ; for(Resource rs:resources){ Properties prop = new Properties() ; prop.load(new FileInputStream(rs.getFile())) ; //properties文件路徑做爲key值,Properties對象做爲value保存在propMap中 propMap.put(rs.getURL().getPath().replace(rootPath, ""), prop) ; } } catch (IOException e) { e.printStackTrace(); } }
2.自定義properties文件註解blog
配置文件properties路徑註解,經過該註解實現properties與java bean之間的綁定。get
@Documented @Target(ElementType.TYPE) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface PropertiesPath { String filePath() default ""; }
配置文件屬性註解,經過該註解實現properties 的key 與java bean屬性之間的綁定it
@Documented @Target(ElementType.FIELD) @Inherited @Retention(RetentionPolicy.RUNTIME) public @interface PropertiesAttribute { public String attributeName() default ""; }
3.掃描package目錄全部帶properties註解的類並經過反射把值set進去io
/** * 掃描class文件並初始化(經過反射) * */ public static void initClass(){ ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); try { if(rootPath==null)rootPath= resourcePatternResolver.getResource("/").getURI().getPath() ; Resource[] resources = resourcePatternResolver.getResources(classPath) ; for(Resource rs:resources){ //獲取class文件名 String claName = rs.getURI().getPath().replace(rootPath, "").replace(".class", "").replace("/", ".") ; //經過反射獲取class引用 Class<?> cla = Class.forName(claName) ; //獲取class註解 PropertiesPath propPath = (PropertiesPath) cla.getAnnotation(PropertiesPath.class) ; //註解爲空則直接返回 if(propPath==null) continue ; //根據註解來獲取properties對象 Properties prop = propMap.get(propPath.filePath()) ; if(prop==null) return ; Field[] fields = cla.getDeclaredFields() ; //掃描當前class對象全部屬性 for(Field fd:fields){ Type type = fd.getGenericType() ; System.out.println(fd.getName()+" "+fd.getModifiers()+" "+type.getTypeName()) ; String attr = null ; //判斷屬性是否有添加註解,有則取註解值 PropertiesAttribute propAttr = fd.getAnnotation(PropertiesAttribute.class) ; if(propAttr!=null) attr=propAttr.attributeName() ; else attr = fd.getName() ; //靜態屬性能夠直接經過set的方法把value注進雲 if(type.getTypeName().equals("java.lang.String")){ fd.set(null, prop.getProperty(attr,null)) ; } else if(type.getTypeName().equals("int")){ fd.setInt(null, Integer.parseInt(prop.getProperty(attr,"0"))) ; } else if(type.getTypeName().equals("boolean")){ fd.setBoolean(null, Boolean.valueOf(prop.getProperty(attr,"false"))) ; }else{ System.out.println("非法數據類型!") ; } //if(fd.getModifiers()==9)fd.set(null, prop.get(attr)) ; } } } catch (Exception e) { e.printStackTrace(); } }
最終實現的效果是
也就是說之後若是有新增配置文件則只須要寫好對應的轉換Bean類便可使用。
如下是完整代碼,固然在須要根據本身項目的須要進行修改