Spring中的@PropertySource注解源碼詳細解析
前言
通常,我們在開發(fā)java spring項目時,會包含多套環(huán)境(profile),并且分別提供了不同環(huán)境下的屬性文件(.properties),在引用屬性文件時,都會用到@PropertySource注解,標注在配置類@Configuration上面,下面主要分析一下@PropertySource注解的處理過程,也就是怎么把配置信息從.properies文件放到environment中的;
1. @PropertySource處理入口
@PropertySource使用時都會和@Configuration放在一起,對@PropertySource的處理也是放在@Configuration解析處理過程中的(對@Configuration的處理過程后面再單獨進行分析),參見源碼如下:
/** * Apply processing and build a complete {@link ConfigurationClass} by reading the * annotations, members and methods from the source class. This method can be called * multiple times as relevant sources are discovered. * @param configClass the configuration class being build * @param sourceClass a source class * @return the superclass, or {@code null} if none found or previously processed */ protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } //......其余部分省略 }
上面for循環(huán)處理的過程,實質(zhì)上主要包含了兩步:
- 解析@PropertySource注解,包括單獨聲明的@PropertySource注解,以及在容器注解@PropertySources value屬性中指定的注解;
- 將解析的@PropertySource注解放到environment中;
下面分別對這兩個過程進行分析;
2. @PropertySource注解解析
解析過程主要封裝到了AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)當中,根據(jù)進去源碼如下:
static Set<AnnotationAttributes> attributesForRepeatable( AnnotationMetadata metadata, String containerClassName, String annotationClassName) { Set<AnnotationAttributes> result = new LinkedHashSet<AnnotationAttributes>(); // Direct annotation present? addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false)); // Container annotation present? Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName, false); if (container != null && container.containsKey("value")) { for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) { addAttributesIfNotNull(result, containedAttributes); } } // Return merged result return Collections.unmodifiableSet(result); } private static void addAttributesIfNotNull(Set<AnnotationAttributes> result, Map<String, Object> attributes) { if (attributes != null) { result.add(AnnotationAttributes.fromMap(attributes)); } }
這里,首先獲取配置類上@PropertySource注解,解析成AnnotationAttributes map對象,放到result中;
然后解析容器注解@PropertySources value屬性值,并將解析的@PropertySource列表放到result中;
這樣@PropertySource注解和@PropertySources容器注解解析完畢;
3. 構(gòu)造ResourcePropertySource對象
這里主要分析一下processPropertySource(propertySource)的過程,源碼如下:
/** * Process the given <code>@PropertySource</code> annotation metadata. * @param propertySource metadata for the <code>@PropertySource</code> annotation found * @throws IOException if loading a property source failed */ 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 = this.resourceLoader.getResource(resolvedLocation); addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding))); } catch (IllegalArgumentException ex) { // Placeholders not resolvable if (ignoreResourceNotFound) { if (logger.isInfoEnabled()) { logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage()); } } else { throw ex; } } catch (IOException ex) { // Resource not found when trying to open it if (ignoreResourceNotFound && (ex instanceof FileNotFoundException || ex instanceof UnknownHostException)) { if (logger.isInfoEnabled()) { logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage()); } } else { throw ex; } } } }
其中@PropertySource的主要屬性value(這里放到了locations中)保存了屬性文件的存放位置,對每一個location的解析主要分為如下3步:
- 解析location中包含的占位符
- 加載Resource對象
- 構(gòu)造ResourcePropertySource對象
- PropertySource加載到environment當中
其中第三步構(gòu)造ResourcePropertySource主要用到了PropertySourceFactory,這里默認實現(xiàn)是DefaultPropertySourceFactory,內(nèi)部實現(xiàn)源碼如下:
/** * The default implementation for {@link PropertySourceFactory}, * wrapping every resource in a {@link ResourcePropertySource}. * * @author Juergen Hoeller * @since 4.3 * @see PropertySourceFactory * @see ResourcePropertySource */ 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)); } }
上面主要通過resource構(gòu)造了ResourcePropertySource對象,其構(gòu)造函數(shù)如下:
/** * 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; }
如上,可見先是由resource構(gòu)造了Peoperties對象,然后構(gòu)造了PropertiesPropertySource父類.....
如下是ResourcePropertySource的繼承結(jié)構(gòu),最終加載的屬性值放入到了PropertySource的成員變量source中;
4. PropertySource配置加載到environment當中
構(gòu)造完ResourcePropertySource對象之后,下面將該對象放入到environment中,源碼如下:
private void addPropertySource(PropertySource<?> propertySource) { String name = propertySource.getName(); MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources(); if (propertySources.contains(name) && this.propertySourceNames.contains(name)) { // We've already added a version, we need to extend it PropertySource<?> existing = propertySources.get(name); 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); } } else { 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); }
注意,這里對于@PropertySource注解獲取的配置屬性放入到了environment的后面,實際在application.properties后面,也即application.properties的優(yōu)先級高于@PropertySource引入的配置,后面單獨對這塊進行分析;
到此這篇關(guān)于Spring中的@PropertySource注解源碼詳細解析的文章就介紹到這了,更多相關(guān)@PropertySource注解源碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Cloud Config 使用本地配置文件方式
這篇文章主要介紹了Spring Cloud Config 使用本地配置文件方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07spring?cloud之eureka高可用集群和服務(wù)分區(qū)解析
這篇文章主要介紹了spring?cloud之eureka高可用集群和服務(wù)分區(qū)解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03IntelliJ IDEA Java項目手動添加依賴 jar 包的方法(圖解)
這篇文章主要介紹了IntelliJ IDEA Java項目手動添加依賴 jar 包,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04