赞
踩
目录
4. PropertySource配置加载到environment当中
通常,我们在开发java spring项目时,会包含多套环境(profile),并且分别提供了不同环境下的属性文件(.properties),在引用属性文件时,都会用到@PropertySource注解,标注在配置类@Configuration上面,下面主要分析一下@PropertySource注解的处理过程,也就是怎么把配置信息从.properies文件放到environment中的;
@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循环处理的过程,实质上主要包含了两步:
下面分别对这两个过程进行分析;
解析过程主要封装到了AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)当中,根据进去源码如下:
- 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容器注解解析完毕;
这里主要分析一下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步:
其中第三步构造ResourcePropertySource主要用到了PropertySourceFactory,这里默认实现是DefaultPropertySourceFactory,内部实现源码如下:
- /**
- * 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构造了ResourcePropertySource对象,其构造函数如下:
- /**
- * 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构造了Peoperties对象,然后构造了PropertiesPropertySource父类.....
如下是ResourcePropertySource的继承结构,最终加载的属性值放入到了PropertySource的成员变量source中;
构造完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的优先级高于@PropertySource引入的配置,后面单独对这块进行分析;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。