溫馨提示×

Spring如何使用PropertyPlaceholderConfigurer讀取文件

發(fā)布時間:2020-09-16 02:46:25 來源:腳本之家 閱讀:123 作者:海向 欄目:編程語言

這篇文章主要介紹了Spring如何使用PropertyPlaceholderConfigurer讀取文件,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下

一. 簡介

大型項目中,我們往往會對我們的系統(tǒng)的配置信息進行統(tǒng)一管理,一般做法是將配置信息配置與一個cfg.properties 的文件中,然后在我們系統(tǒng)初始化的時候,系統(tǒng)自動讀取 cfg.properties 配置文件中的 key value(鍵值對),然后對我們系統(tǒng)進行定制的初始化。

那么一般情況下,我們使用 的 java.util.Properties, 也就是 java 自帶的。往往有一個問題是,每一次加載的時候,我們都需要手工的去讀取這個配置文件,一來編碼麻煩,二來代碼不優(yōu)雅,往往我們也會自己創(chuàng)建一個類來專門讀取,并儲存這些配置信息。

對于 web 項目來說,可以通過相對路徑得到配置文件的路徑,而對于可執(zhí)行項目,在團隊開發(fā)中就需要根據(jù)各自的環(huán)境來指定 properties 配置文件的路徑了。對于這種情況可以將配置文件的路徑放在 java 虛擬機 JVM 的自定義變量(運行時參數(shù))中,例如:-Ddev.config=/dev.properties 尋找的是本機根目錄下

Spring中提供著一個 PropertyPlaceholderConfigurer,這個類是 BeanFactoryPostProcessor 的子類。其主要的原理在是。Spring容器初始化的時候,會讀取 xml 或者 annotation 對 Bean 進行初始化。初始化的時候,這個 PropertyPlaceholderConfigurer 會攔截 Bean 的初始化,初始化的時候會對配置的 ${pname} 進行替換,根據(jù)我們 Properties 中配置的進行替換。從而實現(xiàn)表達式的替換操作 。

二. XML 方式

方式1

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <!-- 對于讀取一個配置文件采取的方案 -->
    <!--<property name="location" value="classpath:db.properties"/>-->

    <!--對于讀取多個配置文件采取的方案-->
    <property name="locations">
      <list>
        <value>classpath:db.properties</value>
        <value>classpath:db2.properties</value>
      </list>
    </property>
  </bean>
</beans>
#db.properties 
jdbc.driverClass==net.sourceforge.jtds.jdbc.Driver 
jdbc.url=jdbc:mysql://localhost:3306/test? 
jdbc.username=anqi 
jdbc.password=123456 
#db2.properties 
name=anqi 
age=23 
import org.junit.Test; import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration("classpath:spring-context.xml") 
public class TestPropertyPlaceHoder2 {   
 @Value("${jdbc.username}")   
 private String username;   
 @Value("${jdbc.password}")   
 private String password;   
 @Value("${name}")   
 private String name;   
 @Value("${age}")   
 private int age;   
 
 @Test   
 public void testResource() {     
  System.out.println("username: " + username);     
  System.out.println("password: " + password);     
  System.out.println("name: " + name);     
  System.out.println("age: " + age);   
 } 
} 
/* username: anqi   password: 123456   name: anqi   age: 23 */ 

方式2

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"      
  <context:property-placeholder location="classpath:db.properties,classpath:db2.properties"/> 

</beans> 

注意:我們知道不論是使用 PropertyPlaceholderConfigurer 還是通過 context:property-placeholder 這種方式進行實現(xiàn),都需要記住,Spring框架不僅僅會讀取我們的配置文件中的鍵值對,而且還會讀取 Jvm 初始化的一下系統(tǒng)的信息。有時候,我們需要將配置 Key 定一套命名規(guī)則 ,例如

jdbc.username

jdbc.password

同時,我們也可以使用下面這種配置方式進行配置,這里我配 NEVER 的意思是不讀取系統(tǒng)配置信息。

<context:property-placeholder location="classpath:db.properties,classpath:db2.properties"   
     system-properties-mode="NEVER"/>
  • SYSTEM_PROPERTIES_MODE_FALLBACK:在解析一個占位符的變量的時候。假設(shè)不能獲取到該變量的值。就會拿系統(tǒng)屬性來嘗試,
  • SYSTEM_PROPERTIES_MODE_OVERRIDE:在解析一個占位符的時候。會先用系統(tǒng)屬性來嘗試,然后才會用指定的屬性文件,
  • SYSTEM_PROPERTIES_MODE_NEVER:從來都不會使用系統(tǒng)屬性來嘗試。

三. Java 編碼方式

采取編碼的方式顯然更加靈活,當我們在做一個項目時,在線下本地跑和在服務器線上跑時,需要的參數(shù)肯定有諸多不同,我們可以通過 xml java 編碼的方式來指定采用哪一個配置方案,同一個配置方案中也可以將線上配置文件的地址放在前面,沒有線上配置文件就采用本地配置的方式來運行項目。

spring-context.xml

<bean>
  <!-- 配置 preoperties文件的加載類 -->
  <bean class="com.anqi.testPropertyPlaceHoder.PropertiesUtil">
    <!-- 配置方案1 優(yōu)先級更高 配置方案1找不到 key 才會去配置方案 2 里面找--> 
    <property name="locations">
      <list>
        <!-- 這里支持多種尋址方式:classpath 和 file -->
        <!-- 推薦使用file的方式引入,這樣可以將配置和代碼分離 -->
        <!--<value>file:/conf/localpro.properties</value>-->
        <value>classpath:db.properties</value>
        <value>classpath:db2.properties</value>
      </list>
    </property>
    <!-- 配置方案2 -->
    <property name="programConfig">
      <list>
        <value>classpath:db3.properties</value>
      </list>
    </property>
  </bean>
</beans>

db.properties

jdbc.driverClass==net.sourceforge.jtds.jdbc.Driver 
jdbc.url=jdbc:mysql://localhost:3306/test? 
jdbc.username=anqi jdbc.
password=123456 
pro=1 
version=db1

db2.properties

name=anqi 
age=23 
pro=2 
version=db2

db3.properties

pro=3 

dev.properties

company=abc version=dev.config 

讀取配置的工具類

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class PropertiesUtil extends PropertyPlaceholderConfigurer {

  private static Resource electResource;

  private static Properties configProperties = new Properties();
  private static Properties programProperties = new Properties();

  public PropertiesUtil() {}

    /**
   * 根據(jù) spring-context 配置文件中的配置,來將項目下對應的 properties 文件加載到系統(tǒng)中
   * 并且經(jīng)過特殊處理 db2.properties 不允許覆蓋掉 db1.properties 中相同的 key
   * @param locations
    */
   public void setLocations(Resource... locations) {
        List<Resource> existResourceList = new ArrayList<>();

        Resource devConfig = getDevConfig();
     if (devConfig != null) {
       existResourceList.add(devConfig);
     }

     Resource resource;
     for(int i = 0; i < locations.length; ++i) {
       resource = locations[i];
       if (resource.exists()) {
         existResourceList.add(resource);
         //dev db.properties db2.properties 
       }
     }

    Collections.reverse(existResourceList);
    //db2.properties db.properties dev

    if (!existResourceList.isEmpty()) {
      electResource = existResourceList.get(existResourceList.size() - 1);
      //dev
    }

    try {
      configProperties.load(electResource.getURL().openStream());
      if (existResourceList != null && existResourceList.size() > 1) {
      for(int i = existResourceList.size() - 2; i >= 0; --i) {
        Properties backupConfig = new Properties();
        //從后往前加載 db1 db2
       backupConfig.load(((Resource)existResourceList.get(i)).getURL().openStream());

       Iterator iterator = backupConfig.keySet().iterator();

       //通過后面新添加的 db3.properties、db4.peoperties 進行更新 db.properties
       //添加沒有的 key 不能覆蓋前面的 key
       while(iterator.hasNext()) {
         Object key = iterator.next();
         if (!configProperties.containsKey(key)) {
           configProperties.put(key, backupConfig.get(key));
         }
       }
      }
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

    /**
    * 將 programConfig 的配置方案加載到 programeConfig 中
    * (即將 db3.properties 加載到 programeConfig)
    * 包含運行時方案(運行時配置優(yōu)先級最高)會覆蓋 key 相同的 value
    * @param locations
    */
   public void setProgramConfig (Resource... locations){

     List<Resource> existResourceList = new ArrayList<>();

     Resource resource;
     for(int i = 0; i < locations.length; ++i) {
       resource = locations[i];
       if (resource.exists()) {
         existResourceList.add(resource);
       }
     }

    if (!existResourceList.isEmpty()) {
      try {
        Iterator<Resource> iterator = existResourceList.iterator();
        while (iterator.hasNext()) {
          resource = iterator.next();
          programProperties.load(resource.getURL().openStream());
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    Resource devConfig = getDevConfig();
    if (devConfig != null) {
      try {
        Properties devProperties = new Properties();
        devProperties.load(devConfig.getURL().openStream());
        Iterator iterator = devProperties.keySet().iterator();

        while(iterator.hasNext()) {
          Object key = iterator.next();
          programProperties.put(String.valueOf(key), 
              devProperties.getProperty(String.valueOf(key), ""));
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  /**
      * 在運行期間傳入配置參數(shù)(可以將配置文件放在本機或服務器上)
      * @return
    */
   private static Resource getDevConfig() {
     String s = System.getProperty("dev.config", "");
     File devConfig = new File(s);
     return !s.trim().equals("") && devConfig.exists() && devConfig.isFile() ? 
           new FileSystemResource(s) : null;
   }

  /**
   * 外部訪問 properties 配置文件中的某個 key
   * @param key
   * @return
      */
   public static String get(String key){
        return programProperties.containsKey(key) ? 
       programProperties.getProperty(key) : configProperties.getProperty(key);
   }

    public static void show() {
    System.out.println("db_1 keys: "+configProperties.keySet());
    System.out.println("db_2 keys: "+programProperties.keySet());
  }
}

測試類

package com.anqi.testPropertyPlaceHoder; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.support.ClassPathXmlApplicationContext;  
public class TestPropertyPlaceHoder {   
  public static void main(String[] args) {     
    ApplicationContext al = new ClassPathXmlApplicationContext("classpath:spring-context.xml");     
    PropertiesUtil.show();     
    System.out.println(PropertiesUtil.get("version")); 

    //-Ddev.config=/dev.properties 傳入運行時參數(shù)
    System.out.println(PropertiesUtil.get("company"));
    System.out.println(PropertiesUtil.get("pro"));
    //db_1 keys: [name, jdbc.password, version, company, jdbc.url, pro, jdbc.driverClass, jdbc.username, age] 
    //db_2 keys: [company, version, pro] 
    //dev.config 
    //abc 
    //3   
  } 
}

以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI