1. 程式人生 > >【Spring4揭祕 基礎2】PropertySource和Enviroment

【Spring4揭祕 基礎2】PropertySource和Enviroment

一 、抽象類PropertySource及子類

PropertySource是一個抽象類,它包含一個source和一個name。source可以是map或其他,通常是一組鍵值對。
這個類結構簡化如下:

public abstract class PropertySource<T> {protected final   

    String name;//屬性源名稱
    protected final T source;//屬性源(比如來自Map,那就是一個Map物件)
    public String getName();  //獲取屬性源的名字  
    public T getSource
(); //獲取屬性源 public boolean containsProperty(String name); //是否包含某個屬性 public abstract Object getProperty(String name); //得到屬性名對應的屬性值 }

主要類體系:

這裡寫圖片描述

實現類1:MapPropertySource

MapPropertySource的source來自於一個Map,這個類結構很簡單,這裡不說。
用法如下:

public static void main(String[] args) {

        Map<String,Object> map=new
HashMap<>(); map.put("name","wang"); map.put("age",23); MapPropertySource source_1=new MapPropertySource("person",map); System.out.println(source_1.getProperty("name"));//wang System.out.println(source_1.getProperty("age"));//23 System.out.println(source_1.getName());//person
System.out.println(source_1.containsProperty("class"));//false }

實現類2:PropertiesPropertySource

source是一個Properties物件,繼承自MapPropertySource。與MapPropertySource用法相同

實現類3:ResourcePropertySource

繼承自PropertiesPropertySource,它的source來自於一個Resource資源。

實現類4:ServletConfigPropertySource

source為ServletConfig物件

實現類5:ServletContextPropertySource

source為ServletContext物件

實現類6 StubPropertySource

臨時作為一個PropertySource的佔位,後期會被真實的PropertySource取代。

實現類7 SystemEnvironmentPropertySource

繼承自MapPropertySource,它的source也是一個map,但來源於系統環境。
【重點】與MapPropertySource不同的是,取值時它將會忽略大小寫,”.”和”_”將會轉化。遇到轉化情況會打印出日誌。用法如下:

SystemEnvironmentPropertySource source =
                new SystemEnvironmentPropertySource("systemEnvironment",(Map)System.getenv());
System.out.println(source.getProperty("PROCESSOR_LEVEL"));
System.out.println(source.getProperty("PROCESSOR_LEVEL".toLowerCase()));
System.out.println(source.getProperty("PROCESSOR.LEVEL"));

輸出如下:

6
09:23:38.833 [main] DEBUG  org.springframework.core.env.SystemEnvironmentPropertySource - PropertySource [systemEnvironment] does not contain 'processor_level', but found equivalent 'PROCESSOR_LEVEL'
6
09:23:38.836 [main] DEBUG org.springframework.core.env.SystemEnvironmentPropertySource - PropertySource [systemEnvironment] does not contain 'PROCESSOR.LEVEL', but found equivalent 'PROCESSOR_LEVEL'
6

實現類9 CompositePropertySource

內部可以儲存多個PropertySource

private final Set<PropertySource<?>> propertySources = new LinkedHashSet<PropertySource<?>>();

取值時依次遍歷這些PropertySource

二、PropertySources

包含多個PropertySource,繼承了Iterable介面,所以它的子類還具有迭代的能力。
介面定義:

public interface PropertySources extends Iterable<PropertySource<?>> {

    boolean contains(String name);//是否包含某個PropertySource

    PropertySource<?> get(String name);//獲取某個PropertySource
}

實現類 MutablePropertySources

它包含了一個CopyOnWriteArrayList集合,用來包含多個PropertySource

private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();

這個類還具有幾個比較重要的方法,用來向集合中加減PropertySource
addFirst
addLast
addBefore
addAfter
remove

三、PropertyResolver介面

實現這個類的介面具有解析PropertySource、根據PropertySource轉換文字中的佔位符的能力

public interface PropertyResolver {  

    //是否包含某個屬性  
    boolean containsProperty(String key);  

    //獲取屬性值 如果找不到返回null   
    String getProperty(String key);  

    //獲取屬性值,如果找不到返回預設值        
    String getProperty(String key, String defaultValue);  

    //獲取指定型別的屬性值,找不到返回null  
    <T> T getProperty(String key, Class<T> targetType);  

    //獲取指定型別的屬性值,找不到返回預設值  
    <T> T getProperty(String key, Class<T> targetType, T defaultValue);  

    //獲取屬性值為某個Class型別,找不到返回null,如果型別不相容將丟擲ConversionException  
    <T> Class<T> getPropertyAsClass(String key, Class<T> targetType);  

    //獲取屬性值,找不到丟擲異常IllegalStateException  
    String getRequiredProperty(String key) throws IllegalStateException;  

    //獲取指定型別的屬性值,找不到丟擲異常IllegalStateException         
    <T> T getRequiredProperty(String key, Class<T> targetType) throws IllegalStateException;  

    //替換文字中的佔位符(${key})到屬性值,找不到不解析  
    String resolvePlaceholders(String text);  

    //替換文字中的佔位符(${key})到屬性值,找不到丟擲異常IllegalArgumentException  
    String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;  

}  

它的實現類主要有兩種:
1 各種Resolver:主要是PropertySourcesPropertyResolver
2 各種Evironment:下一節介紹

## PropertySourcesPropertyResolver示例

MutablePropertySources sources = new MutablePropertySources();
sources.addLast(new MapPropertySource("map", new HashMap<String, Object>() {
            {
                put("name", "wang");
                put("age", 12);
            }
}));//向MutablePropertySources新增一個MapPropertySource

PropertyResolver resolver = new PropertySourcesPropertyResolver(sources);
System.out.println(resolver.containsProperty("name"));//輸出 true
System.out.println(resolver.getProperty("age"));//輸出 12
System.out.println(resolver.resolvePlaceholders("My name is ${name} .I am ${age}."));
//輸出 My name is wang .I am 12.

四、Environment

開發環境,比如JDK環境,系統環境;每個環境都有自己的配置資料,如System.getProperties()可以拿到JDK環境資料、System.getenv()可以拿到系統變數,ServletContext.getInitParameter()可以拿到Servlet環境配置資料。
Spring抽象了一個Environment來表示Spring應用程式環境配置,它整合了各種各樣的外部環境,並且提供統一訪問的方法。

public interface Environment extends PropertyResolver {  
        //得到當前明確啟用的剖面  
    String[] getActiveProfiles();  

        //得到預設啟用的剖面,而不是明確設定啟用的  
    String[] getDefaultProfiles();  

        //是否接受某些剖面  
    boolean acceptsProfiles(String... profiles);  

}  

從API上可以看出,除了可以解析相應的屬性資訊外,還提供了剖面相關的API,目的是: 可以根據剖面有選擇的進行註冊元件/配置。比如對於不同的環境註冊不同的元件/配置(正式機、測試機、開發機等的資料來源配置)
我們再看看它的繼承關係:
這裡寫圖片描述
拋開復雜的繼承關係,發現它的實現類主要有兩個:

StandardEnvironment:標準環境,普通Java應用時使用,會自動註冊System.getProperties() 和 System.getenv()到環境;
StandardServletEnvironment:標準Servlet環境,其繼承了StandardEnvironment,Web應用時使用,除了StandardEnvironment外,會自動註冊ServletConfig(DispatcherServlet)、ServletContext及有選擇性的註冊JNDI例項到環境;
使用示例如下:

//會自動註冊 System.getProperties() 和 System.getenv()  
Environment environment = new StandardEnvironment();  
System.out.println(environment.getProperty("file.encoding"));  

Profile

profile,剖面,大體意思是:我們程式可能從某幾個剖面來執行應用,比如正式機環境、測試機環境、開發機環境等,每個剖面的配置可能不一樣(比如開發機可能使用本地的資料庫測試,正式機使用正式機的資料庫測試)等;因此呢,就需要根據不同的環境選擇不同的配置;
profile有兩種:
預設的:通過環境中“spring.profiles.default”屬性獲取,如果沒有配置預設值是“default”
明確啟用的:通過環境中“spring.profiles.active”獲取
查詢順序是:先進性明確啟用的匹配,如果沒有指定明確啟用的(即集合為空)就找預設的;配置屬性值從Environment讀取。

@Profile()的使用
可以使用在類或方法上,表示這個bean或方法屬於哪個剖面
示例:

@Configuration
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        System.setProperty("spring.profiles.active","dev");
        ApplicationContext context = new AnnotationConfigApplicationContext(Test.class);
        System.out.println(Arrays.asList(context.getBeanNamesForType(String.class)));
    }

    @Bean()
    @Profile("test")
    public String str1() {
        return "str1";
    }

    @Bean
    @Profile("dev")
    public String str2() {
        return "str2";
    }

    @Bean
    public String str3() {
        return "str3";
    }
}

將會輸出[str2, str3]
因為str1的剖面為test,既不是啟用的dev–str2,也不是預設的default—str3

@PropertySource()

Java Config方式的註解,其屬性會自動註冊到相應的Environment

@Configuration  
@PropertySource(value = "classpath:resources.properties", ignoreResourceNotFound = false)  
public class AppConfig {  
}