之前文章簡單的介紹了一下@Value和@PropertySource註解的使用,沒有看過的同學可以點擊查看:
一分鐘學會spring註解之@value註解
一分鐘學會spring註解之@PropertySource註解
今天這篇文章將給大家詳細的介紹一下@PropertySource註解實現原理
首先讓我們一起看下@PropertySource的源碼如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
/**
* 資源的名稱
*/
String name() default "";
/**
* 資源文件路徑,可以是數據多個文件地址
* 可以是classpath地址如:
* "classpath:/com/myco/app.properties"
* 也可以是對應的文件系統地址如:
* "file:/path/to/file"
*/
String[] value();
/**
* 是否忽略文件資源是否存在,默認是false,也就是說配置不存在的文件地址spring啓動將會報錯
*/
boolean ignoreResourceNotFound() default false;
/**
* 這個沒什麼好說的了就是對應的字符編碼了,默認是空值,如果配置文件中有中文應該設置爲utf-8 */
String encoding() default "";
/**
* 關鍵的元素了 讀取對應資源文件的工廠類了 默認的是PropertySourceFactory
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
注意看上面代碼中的註釋,之前文章有演示過讀取classpath中的配置文件,這邊演示一下如何讀取系統目錄中文件如下:
@PropertySource(value={"classpath:/user2.properties","file:/d://user2.properties"},encoding="utf-8",ignoreResourceNotFound=true)
d盤中的user2.properties的配置文件如下:
u.name2=王五
u.age2=25
增加一個user1對象如下:
/**
* 用戶名
*/
@Value("${u.name2}")
private String userName;
/**
* 年齡
*/
@Value("${u.age2}")
private Integer age;
運行測試如下:
實例1 === User [userName=李四, age=29]
實例2 === User [userName=王五, age=25]
從上我們可以發現@PropertySource註解的地址可以是以下兩種:
classpath路徑:"classpath:/com/myco/app.properties"
文件對應路徑:"file:/path/to/file"
接下來我們來詳細的介紹@PropertySource註解底層是如何解析這些配置文件,這個就必須得PropertySourceFactory的具體實現源碼了
進入PropertySourceFactory中你會發現它是一個接口代碼如下:
public interface PropertySourceFactory {
/**
* Create a {@link PropertySource} that wraps the given resource.
* @param name the name of the property source
* @param resource the resource (potentially encoded) to wrap
* @return the new {@link PropertySource} (never {@code null})
* @throws IOException if resource resolution failed
*/
PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException;
}
裏邊只有一個createPropertySource方法,進入其中的實現類中如下:
public class DefaultPropertySourceFactory implements PropertySourceFactory {
@Override
public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}
}
注意,重要的類ResourcePropertySource出現了,進去可以看到兩個主要的構造方法如下:
/**
* Create a PropertySource having the given name based on Properties
* loaded from the given encoded resource.
*/
public ResourcePropertySource(String name, EncodedResource resource) throws IOException {
super(name, PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = getNameForResource(resource.getResource());
}
/**
* Create a PropertySource based on Properties loaded from the given resource.
* The name of the PropertySource will be generated based on the
* {@link Resource#getDescription() description} of the given resource.
*/
public ResourcePropertySource(EncodedResource resource) throws IOException {
super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
this.resourceName = null;
}
在構造方法中你可以發現加載資源的地方PropertiesLoaderUtils.loadProperties(resource),一路進去你可以返現如下代碼:
static void fillProperties(Properties props, EncodedResource resource, PropertiesPersister persister)
throws IOException {
InputStream stream = null;
Reader reader = null;
try {
String filename = resource.getResource().getFilename();
// 加載xml文件
if (filename != null && filename.endsWith(XML_FILE_EXTENSION)) {
stream = resource.getInputStream();
persister.loadFromXml(props, stream);
}
// 判斷是否有需要對應的字符編碼設置 有的話處理對應的InputStream
else if (resource.requiresReader()) {
reader = resource.getReader();
persister.load(props, reader);
}
else {
stream = resource.getInputStream();
persister.load(props, stream);
}
}
}
怎麼樣,是不是可以發現@PropertySource不僅可以解析properties的文件同樣也可以解析xml文件,下邊我們一起來演示一下解析xml的例子吧
首先新增一個user2.xml如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="u.name3">王二小</entry>
<entry key="u.age3">22</entry>
</properties>
配置類增加配置如下:
@PropertySource(value={"classpath:/user.properties","classpath:/user2.xml","file:/d://user2.properties"},encoding="utf-8",ignoreResourceNotFound=false)
測試運行結果如下:
實例1 === User [userName=李四, age=29]
實例2 === User [userName=王二小, age=22]
好了,到目前爲止我們不僅學會了@PropertySource註解的使用,而且瞭解到了其底層的具體實現,做到知其然知其所以然,以及瞭解了其默認的資源解析器PropertySourceFactory,並且你也可以繼承PropertySourceFactory實現自定義的解析器,感興趣的同學可以自己去實現一個自定義解析類
以上是今天文章的所有內容,歡迎大家吐槽
推薦閱讀深入理解spring生命週期與BeanPostProcessor的實現原理
更多優質文章請關注以下公衆號查閱: