Spring的@PropertySource注解源碼解析
一、源碼時序圖
本節(jié),就以源碼時序圖的方式,直觀的感受下@PropertySource注解在Spring源碼層面的執(zhí)行流程。
@PropertySource注解在Spring源碼層面的執(zhí)行流程如圖所示:
由圖可以看出,@PropertySource注解在Spring源碼層面的執(zhí)行流程會涉及到PropertySourceTest類、AnnotationConfigApplicationContext類、AbstractApplicationContext類、PostProcessorRegistrationDelegate類、ConfigurationClassPostProcessor類、ConfigurationClassParser類、PropertySourceRegistry類、PropertySourceProcessor類和DefaultPropertySourceFactory類。具體的源碼執(zhí)行細(xì)節(jié)參見源碼解析部分。
二、源碼解析
@PropertySource注解在Spring源碼層面的執(zhí)行流程,結(jié)合源碼執(zhí)行的時序圖,會理解的更加深刻。
1. 運行案例程序啟動類
在PropertySourceTest類的main()方法中調(diào)用了AnnotationConfigApplicationContext類的構(gòu)造方法,并傳入了PropertySourceConfig類的Class對象來創(chuàng)建IOC容器。
接下來,會進入AnnotationConfigApplicationContext類的構(gòu)造方法。
注意:@PropertySource注解在Spring源碼中的執(zhí)行流程的(2)~(11)步與前面章節(jié)的@Import注解相同,這里不再贅述,直接跳到ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法。
2. 解析ConfigurationClassParser類的doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicatefilter)方法
protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) throws IOException { //#############省略其他代碼################ for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.propertySourceRegistry != null) { this.propertySourceRegistry.processPropertySource(propertySource); } } //#############省略其他代碼################ }
可以看到,在ConfigurationClassParser類的doProcessConfigurationClass()方法中,遍歷獲取到的@PropertySources注解和@PropertySource注解的屬性,并且調(diào)用propertySourceRegistry對象的processPropertySource()方法解析注解屬性的值。
3. 解析PropertySourceRegistry類的processPropertySource(AnnotationAttributes propertySource)方法
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"); Class<? extends PropertySourceFactory> factorClassToUse = (factoryClass != PropertySourceFactory.class ? factoryClass : null); PropertySourceDescriptor descriptor = new PropertySourceDescriptor(Arrays.asList(locations), ignoreResourceNotFound, name, factorClassToUse, encoding); this.propertySourceProcessor.processPropertySource(descriptor); this.descriptors.add(descriptor); }
可以看到,在PropertySourceRegistry類的processPropertySource()方法中,解析@PropertySource注解中的屬性后,將解析出的屬性值封裝到PropertySourceDescriptor對象中,調(diào)用propertySourceProcessor對象的processPropertySource()方法,并傳入PropertySourceDescriptor對象進行進一步處理。
4. 解析PropertySourceProcessor類的processPropertySource(PropertySourceDescriptor descriptor)方法
public void processPropertySource(PropertySourceDescriptor descriptor) throws IOException { String name = descriptor.name(); String encoding = descriptor.encoding(); List<String> locations = descriptor.locations(); Assert.isTrue(locations.size() > 0, "At least one @PropertySource(value) location is required"); boolean ignoreResourceNotFound = descriptor.ignoreResourceNotFound(); PropertySourceFactory factory = (descriptor.propertySourceFactory() != null ? instantiateClass(descriptor.propertySourceFactory()) : DEFAULT_PROPERTY_SOURCE_FACTORY); for (String location : locations) { try { String resolvedLocation = this.environment.resolveRequiredPlaceholders(location); Resource resource = this.resourceLoader.getResource(resolvedLocation); addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); } catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) { //#########省略其他代碼################ } } }
可以看到,在processPropertySource()方法中,會通過@PropertySource注解的屬性值解析出配置文件的內(nèi)容,并且通過factory對象的createPropertySource()方法來創(chuàng)建PropertySource對象。
5. 解析DefaultPropertySourceFactory類的createPropertySource(String name, EncodedResource resource)方法
@Override public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException { return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource)); }
6. 回到PropertySourceProcessor類的processPropertySource(PropertySourceDescriptor descriptor)方法
在PropertySourceProcessor類的processPropertySource()方法中,創(chuàng)建完P(guān)ropertySource對象后,會調(diào)用addPropertySource()方法將獲取到的屬性值添加到Spring的環(huán)境變量中。
7. 解析PropertySourceProcessor類的addPropertySource(PropertySource<?> propertySource)方法
private void addPropertySource(org.springframework.core.env.PropertySource<?> propertySource) { String name = propertySource.getName(); MutablePropertySources propertySources = this.environment.getPropertySources(); if (this.propertySourceNames.contains(name)) { org.springframework.core.env.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(); } CompositePropertySource composite = new CompositePropertySource(name); composite.addPropertySource(newSource); composite.addPropertySource(existing); propertySources.replace(name, composite); } return; } } if (this.propertySourceNames.isEmpty()) { propertySources.addLast(propertySource); } else { String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1); propertySources.addBefore(firstProcessed, propertySource); } this.propertySourceNames.add(name); }
可以看到,在PropertySourceProcessor類的addPropertySource()方法中,會將解析出的配置文件的內(nèi)容添加到Spring的環(huán)境變量中。
具體就是在PropertySourceProcessor類的addPropertySource()方法中,獲取到ConfigurableEnvironment中的MutablePropertySources對象,用來存儲解析出的配置文件中的配置項內(nèi)容。
如果有相同的配置項內(nèi)容,將existing對象強轉(zhuǎn)為CompositePropertySource類型,把新舊相同的配置項進行合并,再放到MutablePropertySources對象中。
后續(xù)就可以通過Spring的環(huán)境變量,來獲取到配置文件中的配置項內(nèi)容。
至此,@PropertySource注解在Spring源碼中的執(zhí)行流程分析完畢。
到此這篇關(guān)于Spring的@PropertySource注解源碼解析的文章就介紹到這了,更多相關(guān)@PropertySource注解源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA2023創(chuàng)建MavenWeb項目并搭建Servlet工程的全過程
Maven提供了大量不同類型的Archetype模板,通過它們可以幫助用戶快速的創(chuàng)建Java項目,這篇文章主要給大家介紹了關(guān)于IDEA2023創(chuàng)建MavenWeb項目并搭建Servlet工程的相關(guān)資料,需要的朋友可以參考下2023-10-10java web在高并發(fā)和分布式下實現(xiàn)訂單號生成唯一的解決方案
這篇文章主要介紹了java web在高并發(fā)和分布式下實現(xiàn)訂單號生成唯一的解決方案,需要的朋友可以參考下2017-11-11詳解Java分布式系統(tǒng)中session一致性問題
這篇文章主要介紹了Java分布式系統(tǒng)中session一致性問題,對分布式系統(tǒng)感興趣的同學(xué),要仔細(xì)看一下2021-04-04MyBatis解決Update動態(tài)SQL逗號的問題
這篇文章主要介紹了MyBatis解決Update動態(tài)SQL逗號的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01SpringBoot整合Web之CORS支持與配置類和 XML配置及注冊攔截器
這篇文章主要介紹了SpringBoot整合Web開發(fā)中CORS支持與配置類和 XML配置及注冊攔截器的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08springboot中非容器類如何獲取配置文件數(shù)據(jù)
這篇文章主要介紹了springboot中非容器類如何獲取配置文件數(shù)據(jù)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01