当前位置:   article > 正文

Spring源码:@PropertySource源码解析_properties location not resolvable

properties location not resolvable

目录

1. @PropertySource处理入口

2. @PropertySource注解解析

3. 构造ResourcePropertySource对象

4. PropertySource配置加载到environment当中


通常,我们在开发java spring项目时,会包含多套环境(profile),并且分别提供了不同环境下的属性文件(.properties),在引用属性文件时,都会用到@PropertySource注解,标注在配置类@Configuration上面,下面主要分析一下@PropertySource注解的处理过程,也就是怎么把配置信息从.properies文件放到environment中的;

1. @PropertySource处理入口

@PropertySource使用时都会和@Configuration放在一起,对@PropertySource的处理也是放在@Configuration解析处理过程中的(对@Configuration的处理过程后面再单独进行分析),参见源码如下:

  1. /**
  2. * Apply processing and build a complete {@link ConfigurationClass} by reading the
  3. * annotations, members and methods from the source class. This method can be called
  4. * multiple times as relevant sources are discovered.
  5. * @param configClass the configuration class being build
  6. * @param sourceClass a source class
  7. * @return the superclass, or {@code null} if none found or previously processed
  8. */
  9. protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
  10. throws IOException {
  11. // Recursively process any member (nested) classes first
  12. processMemberClasses(configClass, sourceClass);
  13. // Process any @PropertySource annotations
  14. for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
  15. sourceClass.getMetadata(), PropertySources.class,
  16. org.springframework.context.annotation.PropertySource.class)) {
  17. if (this.environment instanceof ConfigurableEnvironment) {
  18. processPropertySource(propertySource);
  19. }
  20. else {
  21. logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
  22. "]. Reason: Environment must implement ConfigurableEnvironment");
  23. }
  24. }
  25. //......其余部分省略
  26. }

上面for循环处理的过程,实质上主要包含了两步:

  • 解析@PropertySource注解,包括单独声明的@PropertySource注解,以及在容器注解@PropertySources value属性中指定的注解;
  • 将解析的@PropertySource注解放到environment中;

下面分别对这两个过程进行分析;

2. @PropertySource注解解析

解析过程主要封装到了AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)当中,根据进去源码如下:

  1. static Set<AnnotationAttributes> attributesForRepeatable(
  2. AnnotationMetadata metadata, String containerClassName, String annotationClassName) {
  3. Set<AnnotationAttributes> result = new LinkedHashSet<AnnotationAttributes>();
  4. // Direct annotation present?
  5. addAttributesIfNotNull(result, metadata.getAnnotationAttributes(annotationClassName, false));
  6. // Container annotation present?
  7. Map<String, Object> container = metadata.getAnnotationAttributes(containerClassName, false);
  8. if (container != null && container.containsKey("value")) {
  9. for (Map<String, Object> containedAttributes : (Map<String, Object>[]) container.get("value")) {
  10. addAttributesIfNotNull(result, containedAttributes);
  11. }
  12. }
  13. // Return merged result
  14. return Collections.unmodifiableSet(result);
  15. }
  16. private static void addAttributesIfNotNull(Set<AnnotationAttributes> result, Map<String, Object> attributes) {
  17. if (attributes != null) {
  18. result.add(AnnotationAttributes.fromMap(attributes));
  19. }
  20. }

这里,首先获取配置类上@PropertySource注解,解析成AnnotationAttributes map对象,放到result中;

然后解析容器注解@PropertySources value属性值,并将解析的@PropertySource列表放到result中;

这样@PropertySource注解和@PropertySources容器注解解析完毕;

3. 构造ResourcePropertySource对象

这里主要分析一下processPropertySource(propertySource)的过程,源码如下:

  1. /**
  2. * Process the given <code>@PropertySource</code> annotation metadata.
  3. * @param propertySource metadata for the <code>@PropertySource</code> annotation found
  4. * @throws IOException if loading a property source failed
  5. */
  6. private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
  7. String name = propertySource.getString("name");
  8. if (!StringUtils.hasLength(name)) {
  9. name = null;
  10. }
  11. String encoding = propertySource.getString("encoding");
  12. if (!StringUtils.hasLength(encoding)) {
  13. encoding = null;
  14. }
  15. String[] locations = propertySource.getStringArray("value");
  16. Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
  17. boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
  18. Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
  19. PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
  20. DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));
  21. for (String location : locations) {
  22. try {
  23. String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
  24. Resource resource = this.resourceLoader.getResource(resolvedLocation);
  25. addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
  26. }
  27. catch (IllegalArgumentException ex) {
  28. // Placeholders not resolvable
  29. if (ignoreResourceNotFound) {
  30. if (logger.isInfoEnabled()) {
  31. logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
  32. }
  33. }
  34. else {
  35. throw ex;
  36. }
  37. }
  38. catch (IOException ex) {
  39. // Resource not found when trying to open it
  40. if (ignoreResourceNotFound &&
  41. (ex instanceof FileNotFoundException || ex instanceof UnknownHostException)) {
  42. if (logger.isInfoEnabled()) {
  43. logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
  44. }
  45. }
  46. else {
  47. throw ex;
  48. }
  49. }
  50. }
  51. }

其中@PropertySource的主要属性value(这里放到了locations中)保存了属性文件的存放位置,对每一个location的解析主要分为如下3步:

  • 解析location中包含的占位符
  • 加载Resource对象
  • 构造ResourcePropertySource对象
  • PropertySource加载到environment当中

其中第三步构造ResourcePropertySource主要用到了PropertySourceFactory,这里默认实现是DefaultPropertySourceFactory,内部实现源码如下:

  1. /**
  2. * The default implementation for {@link PropertySourceFactory},
  3. * wrapping every resource in a {@link ResourcePropertySource}.
  4. *
  5. * @author Juergen Hoeller
  6. * @since 4.3
  7. * @see PropertySourceFactory
  8. * @see ResourcePropertySource
  9. */
  10. public class DefaultPropertySourceFactory implements PropertySourceFactory {
  11. @Override
  12. public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
  13. return (name != null ? new ResourcePropertySource(name, resource) : new ResourcePropertySource(resource));
  14. }
  15. }

上面主要通过resource构造了ResourcePropertySource对象,其构造函数如下:

  1. /**
  2. * Create a PropertySource based on Properties loaded from the given resource.
  3. * The name of the PropertySource will be generated based on the
  4. * {@link Resource#getDescription() description} of the given resource.
  5. */
  6. public ResourcePropertySource(EncodedResource resource) throws IOException {
  7. super(getNameForResource(resource.getResource()), PropertiesLoaderUtils.loadProperties(resource));
  8. this.resourceName = null;
  9. }

如上,可见先是由resource构造了Peoperties对象,然后构造了PropertiesPropertySource父类.....

如下是ResourcePropertySource的继承结构,最终加载的属性值放入到了PropertySource的成员变量source中;

4. PropertySource配置加载到environment当中

构造完ResourcePropertySource对象之后,下面将该对象放入到environment中,源码如下:

  1. private void addPropertySource(PropertySource<?> propertySource) {
  2. String name = propertySource.getName();
  3. MutablePropertySources propertySources = ((ConfigurableEnvironment) this.environment).getPropertySources();
  4. if (propertySources.contains(name) && this.propertySourceNames.contains(name)) {
  5. // We've already added a version, we need to extend it
  6. PropertySource<?> existing = propertySources.get(name);
  7. PropertySource<?> newSource = (propertySource instanceof ResourcePropertySource ?
  8. ((ResourcePropertySource) propertySource).withResourceName() : propertySource);
  9. if (existing instanceof CompositePropertySource) {
  10. ((CompositePropertySource) existing).addFirstPropertySource(newSource);
  11. }
  12. else {
  13. if (existing instanceof ResourcePropertySource) {
  14. existing = ((ResourcePropertySource) existing).withResourceName();
  15. }
  16. CompositePropertySource composite = new CompositePropertySource(name);
  17. composite.addPropertySource(newSource);
  18. composite.addPropertySource(existing);
  19. propertySources.replace(name, composite);
  20. }
  21. }
  22. else {
  23. if (this.propertySourceNames.isEmpty()) {
  24. propertySources.addLast(propertySource);
  25. }
  26. else {
  27. String firstProcessed = this.propertySourceNames.get(this.propertySourceNames.size() - 1);
  28. propertySources.addBefore(firstProcessed, propertySource);
  29. }
  30. }
  31. this.propertySourceNames.add(name);
  32. }

注意,这里对于@PropertySource注解获取的配置属性放入到了environment的后面,实际在application.properties后面,也即application.properties的优先级高于@PropertySource引入的配置,后面单独对这块进行分析;

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/534008
推荐阅读
相关标签
  

闽ICP备14008679号