Spring中的@PropertySource注解源碼詳解
@PropertySource注解使用
@PropertySource注解用于指定資源文件讀取的位置,它不僅能讀取properties文件,也能讀取xml文件,并且通過yaml解析器,配合自定義PropertySourceFactory實(shí)現(xiàn)解析yaml文件
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
//資源名稱,為空則根據(jù)資源的描述符生成
String name() default "";
/**
*資源路徑
*classpath:application.properties
*file:/
*/
String[] value();
//是否忽略資源不存在的情況,如果不忽略,當(dāng)資源不存在時報(bào)錯
boolean ignoreResourceNotFound() default false;
//指定資源文件的編碼格式
String encoding() default "";
//資源工廠
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}源碼解析
在bean實(shí)例化之前有一個ConfigurationClassPostProcessor類,會操作BeanDefinition并且加入到BeanDefinitionRegistry中,它的優(yōu)先級在所有的BeanDefinitionRegistryPostProcessor里面是最低的。
優(yōu)先級:最低
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE; // within PriorityOrdered
}
會在這個類中進(jìn)行相關(guān)注解的解析操作,在進(jìn)行@PropertySource注解解析的時候要借助ConfigurationClassParser的parse方法
在進(jìn)行@PropertySource注解解析之前,需要拿到兩個對象ConfigurationClass和SourceClass
bean的元數(shù)據(jù)信息(bd.getMetadata),和beanName,封裝成ConfigurationClass對象
new ConfigurationClass(metadata, beanName)
SourceClass這個對象理解為跟類或者接口對應(yīng),然后把metadata對象包裝進(jìn)去
SourceClass sourceClass = asSourceClass(configClass, filter);
接下來正式進(jìn)入解析流程
for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)) {
if (this.environment instanceof ConfigurableEnvironment) {
//核心邏輯
processPropertySource(propertySource);
}
else {
logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
"]. Reason: Environment must implement ConfigurableEnvironment");
}
}
拿到類上面的所有PropertySource注解的值A(chǔ)nnotationAttributes,放到processPropertySource方法中進(jìn)行解析
private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
String name = propertySource.getString("name");
if (!StringUtils.hasLength(name)) {
name = null;
}
String encoding = propertySource.getString("encoding");
if (!StringUtils.hasLength(encoding)) {
encoding = null;
}
//獲取配置文件路徑
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
for (String location : locations) {
try {
//替換占位符
String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
//流的方式加載配置文件并封裝成Resource對象
Resource resource = this.resourceLoader.getResource(resolvedLocation);
//加載Resource中的配置屬性封裝成Properties對象中,并創(chuàng)建PropertySource對象加入到Environment對象中
addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
}
catch (IllegalArgumentException | FileNotFoundException | UnknownHostException ex) {
// Placeholders not resolvable or resource not found when trying to open it
if (ignoreResourceNotFound) {
if (logger.isInfoEnabled()) {
logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
}
}
else {
throw ex;
}
}
}
}
這里看一下createPropertySource方法,就是將配置文件中的內(nèi)容封裝成ResourcePropertySource對象,作為屬性源
public PropertySource<?> createPropertySource(@Nullable String name, EncodedResource resource) throws IOException {
return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
}
屬性源拿到之后,要統(tǒng)一交給Environment管理,調(diào)用addPropertySource方法
private void addPropertySource(PropertySource<?> propertySource) {
String name = propertySource.getName();
//獲取Environment對象中的MutablePropertySources
MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
//如果已經(jīng)存在了該配置文件的PropertySource則合并久的
if (this.propertySourceNames.contains(name)) {
// We've already added a version, we need to extend it
PropertySource<?> existing = propertySources.get(name);
if (existing != null) {
PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
((ResourcePropertySource) propertySource).withResourceName() : propertySource);
//合并二次后的類型
if (existing instanceof CompositePropertySource) {
((CompositePropertySource) existing).addFirstPropertySource(newSource);
}
else {
if (existing instanceof ResourcePropertySource) {
existing = ((ResourcePropertySource) existing).withResourceName();
}
//其實(shí)就是CompositePropertySource里面有一個Set,Set里面裝了新和舊的PropertySource對象
CompositePropertySource composite = new CompositePropertySource(name);
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);
}
return;
}
}
if (this.propertySourceNames.isEmpty()) {
propertySources.addLast(propertySource);
}
else {
//用于計(jì)算插入的位置index
String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
//吧propertySource對象存入MutablePropertySources的list中
propertySources.addBefore(firstProcessed, propertySource);
}
this.propertySourceNames.add(name);
}
拿到Environment中的MutablePropertySources,用來放置拿到的屬性源。 如果有相同的屬性源,則升級成CompositePropertySource,把新舊相同的屬性源進(jìn)行合并,再放到MutablePropertySources中
看看CompositePropertySource類的內(nèi)部 有Set<PropertySource<?>> propertySources = new LinkedHashSet<>();用來放置屬性源 也重寫了getProperty方法
public Object getProperty(String name) {
for (PropertySource<?> propertySource : this.propertySources) {
Object candidate = propertySource.getProperty(name);
if (candidate != null) {
return candidate;
}
}
return null;
}
@PropertySource注解中配置的屬性源都交給Environment管理
到此這篇關(guān)于Spring中的@PropertySource注解源碼詳解的文章就介紹到這了,更多相關(guān)@PropertySource注解源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
教你怎么用Java數(shù)組和鏈表實(shí)現(xiàn)棧
本篇文章為大家詳細(xì)介紹了怎么用Java數(shù)組和鏈表實(shí)現(xiàn)棧,文中有非常詳細(xì)的代碼示例及注釋,對正在學(xué)習(xí)java的小伙伴們很有幫助,需要的朋友可以參考下2021-05-05
Java實(shí)現(xiàn)DES加密與解密,md5加密以及Java實(shí)現(xiàn)MD5加密解密類
這篇文章主要介紹了Java實(shí)現(xiàn)DES加密與解密,md5加密以及Java實(shí)現(xiàn)MD5加密解密類 ,需要的朋友可以參考下2015-11-11
idea配置springboot熱部署終極解決辦法(解決熱部署失效問題)
這篇文章主要介紹了idea配置springboot熱部署終極解決辦法(解決熱部署失效問題),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧2020-07-07
Springboot集成MongoDB無認(rèn)證與開啟認(rèn)證的配置方式
本文主要介紹了Springboot集成MongoDB無認(rèn)證與開啟認(rèn)證的配置方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-03-03
java 獲取冒號后面的參數(shù)(正則)實(shí)現(xiàn)代碼
這篇文章主要介紹了java 獲取冒號后面的參數(shù)(正則)實(shí)現(xiàn)代碼,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
java集合——Java中的equals和hashCode方法詳解
本篇文章詳細(xì)介紹了Java中的equals和hashCode方法詳解,Object 類是所有類的父類,非常具有實(shí)用價值,需要的朋友可以參考下。2016-10-10
SpringBoot整合Netty實(shí)現(xiàn)WebSocket的示例代碼
本文主要介紹了SpringBoot整合Netty實(shí)現(xiàn)WebSocket的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05

