当前位置:   article > 正文

spring 中的属性解析器 PropertyResolver

spring 中的属性解析器 PropertyResolver

我们知道,在 spring 中可以使用占位符,格式如 "${}",大括号中间放置待替换的占位符,待使用值时根据配置的属性解析器进行解析。但具体是如何操作的,且看本文来进行分析。

PropertyResolver

这是一个针对任意底层资源进行属性解析的接口,内部定义了根据 key 获取属性和解析占位符的相关抽象方法。

  1. // 方法1
  2. String resolvePlaceholders(String text);
  3. // 方法2
  4. String resolveRequiredPlaceholders(String text) throws IllegalArgumentException;

方法1 和 方法2 都用来解析 text 字符串中的 "${...}" 占位符,区别在于遇到解析不了的占位符,方法1 不做处理,方法2 会抛出异常。

PropertyResolver 在 spring 中的实现类,主要有以下几个:

  • StandardEnvironment
  • StandardServletEnvironment  
  • PropertySourcesPropertyResolver

其中 StandardServletEnvironment 是在 spring MVC 中使用,继承 StandardEnvironment。下面来看看 StandardEnvironment 和 PropertySourcesPropertyResolver 中的具体实现。

StandardEnvironment

在创建 spring 应用时,会给 AbstractApplicationContext 中的属性 environment 进行赋值。

  1. // AbstractApplicationContext
  2. @Override
  3. public ConfigurableEnvironment getEnvironment() {
  4. if (this.environment == null) {
  5. this.environment = createEnvironment();
  6. }
  7. return this.environment;
  8. }
  9. protected ConfigurableEnvironment createEnvironment() {
  10. return new StandardEnvironment();
  11. }

可以看到创建了一个 StandardEnvironment。类关系图如下:

继承 AbstractEnvironment,所以在执行构造方法时会调用父类的无参构造。

  1. //StandardEnvironment 父类 AbstractEnvironment
  2. public AbstractEnvironment() {
  3. this(new MutablePropertySources());
  4. }
  5. protected AbstractEnvironment(MutablePropertySources propertySources) {
  6. this.propertySources = propertySources;
  7. this.propertyResolver = createPropertyResolver(propertySources);
  8. customizePropertySources(propertySources);
  9. }
  10. protected ConfigurablePropertyResolver createPropertyResolver(MutablePropertySources propertySources) {
  11. return new PropertySourcesPropertyResolver(propertySources);
  12. }

 可以看到,创建了一个 PropertySourcesPropertyResolver 实例对象,并赋值给 AbstractEnvironment 中属性 propertyResolver。接着调用了方法 customizePropertySources,根据方法名可知,自定义属性源,由子类 StandardEnvironment 进行重写。

  1. @Override
  2. protected void customizePropertySources(MutablePropertySources propertySources) {
  3. propertySources.addLast(
  4. new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
  5. propertySources.addLast(
  6. new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
  7. }

可以看到,StandardEnvironment 定义的 PropertySources 只有两个,一个获取的是系统属性、一个获取的是系统环境变量。并且这个 propertySources 在创建 PropertySourcesPropertyResolver 时作为参数,传递给了 PropertySourcesPropertyResolver 中属性 propertySources。

查看 StandardEnvironment 中对 PropertyResolver 中接口方法的实现,都是在 AbstractEnvironemnt 类中,委托给属性 propertyResolver 来实现的。

  1. @Override
  2. @Nullable
  3. public String getProperty(String key) {
  4. return this.propertyResolver.getProperty(key);
  5. }
  6. @Override
  7. public String resolvePlaceholders(String text) {
  8. return this.propertyResolver.resolvePlaceholders(text);
  9. }
  10. @Override
  11. public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
  12. return this.propertyResolver.resolveRequiredPlaceholders(text);
  13. }

这样,就实现了对属性解析的统一,即都是通过 PropertySourcesPropertyResolver 类来操作的

PropertySourcesPropertyResolver

继承 AbstractPropertyResolver,对占位符的解析,在 AbstractPropertyResolver 中实现。

  1. @Override
  2. public String resolvePlaceholders(String text) {
  3. if (this.nonStrictHelper == null) {
  4. this.nonStrictHelper = createPlaceholderHelper(true);
  5. }
  6. return doResolvePlaceholders(text, this.nonStrictHelper);
  7. }
  8. @Override
  9. public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
  10. if (this.strictHelper == null) {
  11. this.strictHelper = createPlaceholderHelper(false);
  12. }
  13. return doResolvePlaceholders(text, this.strictHelper);
  14. }

第一次调用时,创建不同的 PlaceholderHelper,并赋值。

  1. private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
  2. return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
  3. this.valueSeparator, ignoreUnresolvablePlaceholders);
  4. }
  5. private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
  6. return helper.replacePlaceholders(text, this::getPropertyAsRawString);
  7. }
  1. public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
  2. Assert.notNull(value, "'value' must not be null");
  3. return parseStringValue(value, placeholderResolver, null);
  4. }
  5. protected String parseStringValue(
  6. String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
  7. // 判断是否存在占位符前缀,即 "${",不存在前缀,认为不存在占位符,直接返回参数 value
  8. int startIndex = value.indexOf(this.placeholderPrefix);
  9. if (startIndex == -1) {
  10. return value;
  11. }
  12. StringBuilder result = new StringBuilder(value);
  13. // 存在前缀
  14. while (startIndex != -1) {
  15. // 找到最后一个后缀索引
  16. int endIndex = findPlaceholderEndIndex(result, startIndex);
  17. if (endIndex != -1) {
  18. // 截取占位符
  19. String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
  20. String originalPlaceholder = placeholder;
  21. // 处理循环占位符引用
  22. if (visitedPlaceholders == null) {
  23. visitedPlaceholders = new HashSet<>(4);
  24. }
  25. if (!visitedPlaceholders.add(originalPlaceholder)) {
  26. throw new IllegalArgumentException(
  27. "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
  28. }
  29. // Recursive invocation, parsing placeholders contained in the placeholder key.
  30. // 递归调用,解析 placeholder 中的 占位符
  31. placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
  32. // Now obtain the value for the fully resolved key...
  33. // 解析获取 propVal
  34. String propVal = placeholderResolver.resolvePlaceholder(placeholder);
  35. // propVal 为 null,valueSeparator 不为 null,valueSeparator 默认 ":"
  36. // 此种格式即 "${xxx:123}",其中 xxx 为 actualPlaceholder,123 为 defaultValue,接着使用 placeholderResolver 解析 actualPlaceholder,
  37. // 不存在,将 defaultValue 赋值给 propVal
  38. if (propVal == null && this.valueSeparator != null) {
  39. int separatorIndex = placeholder.indexOf(this.valueSeparator);
  40. if (separatorIndex != -1) {
  41. String actualPlaceholder = placeholder.substring(0, separatorIndex);
  42. String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
  43. propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
  44. if (propVal == null) {
  45. propVal = defaultValue;
  46. }
  47. }
  48. }
  49. if (propVal != null) {
  50. // Recursive invocation, parsing placeholders contained in the
  51. // previously resolved placeholder value.
  52. // 对解析出的 propVal 进行递归解析,处理 占位符嵌套,即 配置的 propVal 中也肯能存在占位符
  53. // 解析后 将 result 中 占位字符串 以解析出的 propVal 替换,接着更新索引
  54. propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
  55. result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
  56. if (logger.isTraceEnabled()) {
  57. logger.trace("Resolved placeholder '" + placeholder + "'");
  58. }
  59. startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
  60. }
  61. // ignoreUnresolvablePlaceholders 由创建 PropertyPlaceholderHelper 实例时参数传入
  62. // nonStrictHelper 中为 true,表示可以忽略无法解析的占位符
  63. // strictHelper 中为 false,此时如果 propVal 为 null,就会抛出异常
  64. else if (this.ignoreUnresolvablePlaceholders) {
  65. // Proceed with unprocessed value.
  66. // 向后查找,更新 startIndex
  67. startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
  68. }
  69. else {
  70. throw new IllegalArgumentException("Could not resolve placeholder '" +
  71. placeholder + "'" + " in value \"" + value + "\"");
  72. }
  73. visitedPlaceholders.remove(originalPlaceholder);
  74. }
  75. else {
  76. // 存在前缀,但不存在后缀,将前缀索引置为 -1,下一次跳出循环
  77. startIndex = -1;
  78. }
  79. }
  80. return result.toString();
  81. }
  82. //找到 buf 中占位符后缀对应的最后一个索引
  83. private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
  84. int index = startIndex + this.placeholderPrefix.length();
  85. int withinNestedPlaceholder = 0;
  86. while (index < buf.length()) {
  87. // placeholderSuffix "}"
  88. // // 匹配到了后缀,判断是否存在内嵌,存在,内嵌数量减 1,重置索引,不存在内嵌,直接返回
  89. if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
  90. if (withinNestedPlaceholder > 0) {
  91. withinNestedPlaceholder--;
  92. index = index + this.placeholderSuffix.length();
  93. }
  94. else {
  95. return index;
  96. }
  97. }
  98. // simplePrefix "{"
  99. // 判断是否存在内嵌的占位符
  100. else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
  101. withinNestedPlaceholder++;
  102. index = index + this.simplePrefix.length();
  103. }
  104. else {
  105. index++;
  106. }
  107. }
  108. return -1;
  109. }
  110. public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {
  111. if (index + substring.length() > str.length()) {
  112. return false;
  113. }
  114. // 逐个字符比较,当 substring 为 "}" 或 "{" 时,就是比较 str 中索引为 index 的字符和 substring 对应的字符是否相等
  115. for (int i = 0; i < substring.length(); i++) {
  116. if (str.charAt(index + i) != substring.charAt(i)) {
  117. return false;
  118. }
  119. }
  120. return true;
  121. }

 可以看到,具体的解析是由 PropertyPlaceholderHelper 来完成的。先解析出占位符 placeholder,再通过传入的 lambda 表达式,作为 placeholderResolver,解析 placeholder,得到属性值 propVal。

下面看看这个传入的 lambda 表达式,是一个抽象方法,由子类 PropertySourcesPropertyResolver 实现。

  1. @Override
  2. @Nullable
  3. protected String getPropertyAsRawString(String key) {
  4. return getProperty(key, String.class, false);
  5. }
  6. @Nullable
  7. protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
  8. if (this.propertySources != null) {
  9. for (PropertySource<?> propertySource : this.propertySources) {
  10. if (logger.isTraceEnabled()) {
  11. logger.trace("Searching for key '" + key + "' in PropertySource '" +
  12. propertySource.getName() + "'");
  13. }
  14. Object value = propertySource.getProperty(key);
  15. if (value != null) {
  16. if (resolveNestedPlaceholders && value instanceof String) {
  17. value = resolveNestedPlaceholders((String) value);
  18. }
  19. logKeyFound(key, propertySource, value);
  20. return convertValueIfNecessary(value, targetValueType);
  21. }
  22. }
  23. }
  24. if (logger.isTraceEnabled()) {
  25. logger.trace("Could not find key '" + key + "' in any property source");
  26. }
  27. return null;
  28. }

遍历注册的 propertySources,获取 value,默认注册的只有系统属性和环境变量,所以占位符只有是在系统属性和环境变量中出现的 key,才可以解析出对应的 value。这么看来这个功能太鸡肋了,如果想丰富这个功能,必须增加 PropertySource。

PropertySourcesPlaceholderConfigurer

从类关系可以看到,这是一个 BeanFactoryPostProcessor 子类,提供了通过 spring Environment 中设置的 PropertySources 来解析 BeanDefinition 定义时的 PropertyValues 和 @Value 中的 "${...}" 占位符。

添加 PropertySourcesPlaceholderConfigurer 有两种方式:

<context:property-placeholder location="config.properties"/>
  1. <bean id="placeholderConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
  2. <property name="locations">
  3. <list>
  4. <value>config.properties</value>
  5. </list>
  6. </property>
  7. </bean>

这两种配置的结果是等价的,所不同的只是解析时的方式不同,第一种通过自定义标签解析,第二种通过默认标签解析。下面来看下第一种配置是如何操作的。

通过 spring 对于 XML 中自定义标签的解析,先去 spring-context 模块下 META-INF/spring.handlers 中找到 context 对应的 handler 为 ContextNamespaceHandler。在 ContextNamespaceHandler#init 方法中注册的 property-placeholder 解析器为 PropertyPlaceholderBeanDefinitionParser

property-placeholder 解析

解析入口在 AbstractBeanDefinitionParser#parse 中。

  1. @Override
  2. @Nullable
  3. public final BeanDefinition parse(Element element, ParserContext parserContext) {
  4. // 解析 BeanDefinition
  5. AbstractBeanDefinition definition = parseInternal(element, parserContext);
  6. if (definition != null && !parserContext.isNested()) {
  7. try {
  8. // 解析 id
  9. String id = resolveId(element, definition, parserContext);
  10. if (!StringUtils.hasText(id)) {
  11. ...
  12. }
  13. String[] aliases = null;
  14. if (shouldParseNameAsAliases()) {
  15. // 解析 name,作为 bean 的别名
  16. String name = element.getAttribute(NAME_ATTRIBUTE);
  17. if (StringUtils.hasLength(name)) {
  18. aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
  19. }
  20. }
  21. // 封装 BeanDefinitionHolder,注册 BeanDefinition,存在 aliases,注册 aliases
  22. BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
  23. registerBeanDefinition(holder, parserContext.getRegistry());
  24. if (shouldFireEvents()) {
  25. BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
  26. postProcessComponentDefinition(componentDefinition);
  27. parserContext.registerComponent(componentDefinition);
  28. }
  29. }
  30. catch (BeanDefinitionStoreException ex) {
  31. ...
  32. }
  33. }
  34. return definition;
  35. }

由 AbstractBeanDefinitionParser#parse 方法调入 AbstractSingleBeanDefinitionParser#parseInternal 方法中。 

  1. @Override
  2. protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
  3. BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
  4. String parentName = getParentName(element);
  5. if (parentName != null) {
  6. builder.getRawBeanDefinition().setParentName(parentName);
  7. }
  8. Class<?> beanClass = getBeanClass(element);
  9. if (beanClass != null) {
  10. builder.getRawBeanDefinition().setBeanClass(beanClass);
  11. }
  12. else {
  13. String beanClassName = getBeanClassName(element);
  14. if (beanClassName != null) {
  15. builder.getRawBeanDefinition().setBeanClassName(beanClassName);
  16. }
  17. }
  18. builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
  19. BeanDefinition containingBd = parserContext.getContainingBeanDefinition();
  20. if (containingBd != null) {
  21. // Inner bean definition must receive same scope as containing bean.
  22. builder.setScope(containingBd.getScope());
  23. }
  24. if (parserContext.isDefaultLazyInit()) {
  25. // Default-lazy-init applies to custom bean definitions as well.
  26. builder.setLazyInit(true);
  27. }
  28. doParse(element, parserContext, builder);
  29. return builder.getBeanDefinition();
  30. }

PropertyPlaceholderBeanDefinitionParser 重写了 getBeanClass 方法。

  1. @Override
  2. @SuppressWarnings("deprecation")
  3. protected Class<?> getBeanClass(Element element) {
  4. // spring 3.1 之后,默认采用
  5. if (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) {
  6. return PropertySourcesPlaceholderConfigurer.class;
  7. }
  8. // 适配老版本
  9. return org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.class;
  10. }

所以创建的 GenericBeanDefinition 中 beanClass 为 PropertySourcesPlaceholderConfigurer.class,设置完 beanClass 后,执行 doParser,进入 PropertyPlaceholderBeanDefinitionParser#doParser。

  1. // PropertyPlaceholderBeanDefinitionParser
  2. @Override
  3. protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
  4. super.doParse(element, parserContext, builder);
  5. builder.addPropertyValue("ignoreUnresolvablePlaceholders",
  6. Boolean.valueOf(element.getAttribute("ignore-unresolvable")));
  7. String systemPropertiesModeName = element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE);
  8. if (StringUtils.hasLength(systemPropertiesModeName) &&
  9. !systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {
  10. builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_" + systemPropertiesModeName);
  11. }
  12. // value-spearator 默认 ":"
  13. if (element.hasAttribute("value-separator")) {
  14. builder.addPropertyValue("valueSeparator", element.getAttribute("value-separator"));
  15. }
  16. if (element.hasAttribute("trim-values")) {
  17. builder.addPropertyValue("trimValues", element.getAttribute("trim-values"));
  18. }
  19. if (element.hasAttribute("null-value")) {
  20. builder.addPropertyValue("nullValue", element.getAttribute("null-value"));
  21. }
  22. }
  1. // AbstractPropertyLoadingBeanDefinitionParser
  2. @Override
  3. protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
  4. String location = element.getAttribute("location");
  5. if (StringUtils.hasLength(location)) {
  6. // resolvePlaceholders 遇见无法解析的占位符跳过
  7. location = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(location);
  8. String[] locations = StringUtils.commaDelimitedListToStringArray(location);
  9. builder.addPropertyValue("locations", locations);
  10. }
  11. String propertiesRef = element.getAttribute("properties-ref");
  12. if (StringUtils.hasLength(propertiesRef)) {
  13. builder.addPropertyReference("properties", propertiesRef);
  14. }
  15. String fileEncoding = element.getAttribute("file-encoding");
  16. if (StringUtils.hasLength(fileEncoding)) {
  17. builder.addPropertyValue("fileEncoding", fileEncoding);
  18. }
  19. String order = element.getAttribute("order");
  20. if (StringUtils.hasLength(order)) {
  21. builder.addPropertyValue("order", Integer.valueOf(order));
  22. }
  23. builder.addPropertyValue("ignoreResourceNotFound",
  24. Boolean.valueOf(element.getAttribute("ignore-resource-not-found")));
  25. builder.addPropertyValue("localOverride",
  26. Boolean.valueOf(element.getAttribute("local-override")));
  27. builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
  28. }

可以看到,又调用到了父类 AbstractPropertyLoadingBeanDefinitionParser 中,此处会进行 location 的解析。此处 location 也支持 "${...}" 占位符配置,但由于此时的 Environment 中注册的 PropertySources 只有系统变量和环境变量,所以此处可替换的占位符很有限。

也可以看到此处对 location 中占位符的解析比较宽松,选用的是 resolvePlaceholders,即没有默认值的无法解析的占位符将被忽略,并原封不动地传递。

location 支持以 "," 分隔的形式配置多个资源文件,最后添加 beanDefinition 的 PropertyValues,key 为 locations。

解析得到 BeanDefinition 后,进入 AbstractBeanDefinitionParser#resolveId。

  1. protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
  2. throws BeanDefinitionStoreException {
  3. if (shouldGenerateId()) {
  4. return parserContext.getReaderContext().generateBeanName(definition);
  5. }
  6. else {
  7. String id = element.getAttribute(ID_ATTRIBUTE);
  8. if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
  9. id = parserContext.getReaderContext().generateBeanName(definition);
  10. }
  11. return id;
  12. }
  13. }

针对 PropertySourcesPlaceholderConfigure,默认会生成 id。此处采用默认的 Bean 名称生成策略生成 beanName。

  1. // DefaultBeanNameGenerator
  2. @Override
  3. public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
  4. return BeanDefinitionReaderUtils.generateBeanName(definition, registry);
  5. }
  1. public static String generateBeanName(
  2. BeanDefinition definition, BeanDefinitionRegistry registry, boolean isInnerBean)
  3. throws BeanDefinitionStoreException {
  4. String generatedBeanName = definition.getBeanClassName();
  5. if (generatedBeanName == null) {
  6. if (definition.getParentName() != null) {
  7. generatedBeanName = definition.getParentName() + "$child";
  8. }
  9. else if (definition.getFactoryBeanName() != null) {
  10. generatedBeanName = definition.getFactoryBeanName() + "$created";
  11. }
  12. }
  13. if (!StringUtils.hasText(generatedBeanName)) {
  14. ...
  15. }
  16. if (isInnerBean) {
  17. return generatedBeanName + GENERATED_BEAN_NAME_SEPARATOR + ObjectUtils.getIdentityHexString(definition);
  18. }
  19. return uniqueBeanName(generatedBeanName, registry);
  20. }
  21. public static String uniqueBeanName(String beanName, BeanDefinitionRegistry registry) {
  22. String id = beanName;
  23. int counter = -1;
  24. // 自增 counter 直到生成的 id 在 BeanFactory 中 唯一
  25. String prefix = beanName + GENERATED_BEAN_NAME_SEPARATOR;
  26. while (counter == -1 || registry.containsBeanDefinition(id)) {
  27. counter++;
  28. id = prefix + counter;
  29. }
  30. return id;
  31. }

逻辑很简单,最后生成的 id 如:org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0,之后在 AbstractBeanDefinitionParser#parse 方法中将此 id 作为 beanName,对 beanDefinition 进行注册。

至此,完成了对 context 标签下 property-placeholder 的解析。

实例化

从上面的介绍可以知道,完成解析后,注册了一个名称为 org.springframework.context.support.PropertySourcesPlaceholderConfigurer#0 的 BeanDefinition,那么这个 bean 是什么时候实例化的呢?

从一开始介绍类结构时,我们说过这是一个 BeanFactoryPostProcessor 子类,所以它会在 AbstractApplicationContext#refersh 中执行 invokeBeanFactoryPostProcessors 时进行实例化。

  1. public static void invokeBeanFactoryPostProcessors(
  2. ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
  3. // 处理 BeanDefinitionRegistryPostProcessors
  4. Set<String> processedBeans = new HashSet<>();
  5. ...
  6. // 处理 BeanFactoryPostProcessor
  7. String[] postProcessorNames =
  8. beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
  9. // 将获得的 BeanFactoryPostProcessor 按 PriorityOrdered、Ordered 和 剩下的分类
  10. List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
  11. List<String> orderedPostProcessorNames = new ArrayList<>();
  12. List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  13. for (String ppName : postProcessorNames) {
  14. if (processedBeans.contains(ppName)) {
  15. // 跳过已经处理过的
  16. }
  17. else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
  18. priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
  19. }
  20. else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
  21. orderedPostProcessorNames.add(ppName);
  22. }
  23. else {
  24. nonOrderedPostProcessorNames.add(ppName);
  25. }
  26. }
  27. // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
  28. // 首先,调用实现了 PriorityOrdered 的 BeanFactoryPostProcessor
  29. sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  30. invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
  31. // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
  32. // 其次,调用实现了 Ordered 的 BeanFactoryPostProcessor
  33. List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
  34. for (String postProcessorName : orderedPostProcessorNames) {
  35. orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  36. }
  37. sortPostProcessors(orderedPostProcessors, beanFactory);
  38. invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
  39. // 最后,调用剩下的 BeanFactoryPostProcessor
  40. List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
  41. for (String postProcessorName : nonOrderedPostProcessorNames) {
  42. nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
  43. }
  44. invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
  45. // Clear cached merged bean definitions since the post-processors might have
  46. // modified the original metadata, e.g. replacing placeholders in values...
  47. beanFactory.clearMetadataCache();
  48. }

PropertySourcesPlaceholderConfigurer 实现了 PriorityOrdered,所以,会首先完成 PropertySourcesPlaceholderConfigurer 的实例化。调用 beanFactory.getBean 调用获取实例对象。

我们知道,在实例化之后,会对创建的半成品对象进行属性填充,下面来看看针对 PropertySourcesPlaceholderConfigurer 的属性填充。

applyPropertyValues

从前面的 property-placeholder 解析可知,将配置的 location 最后解析成了属性名为 locations 的 String 数组,所以在此处,当调用 valueResolver.resolveValueIfNecessary 时,会调用 BeanDefinitionValueResolver#evaluate --> BeanDefinitionValueResolver#doEvaluate               --> AbstractBeanFactory#evaluateBeanDefinitionString

  1. @Nullable
  2. protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
  3. if (this.beanExpressionResolver == null) {
  4. return value;
  5. }
  6. Scope scope = null;
  7. if (beanDefinition != null) {
  8. String scopeName = beanDefinition.getScope();
  9. if (scopeName != null) {
  10. scope = getRegisteredScope(scopeName);
  11. }
  12. }
  13. return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
  14. }

可以看到最终通过为 beanFactory 设置的 beanExpressionResolver 来进行处理,即 StandardBeanExpressionResolver,这是 Spring EL 表达式的标准实现,默认解析 #{...},此时若为 ${...},并不会对其解析。

接着,判断 locations 是一个可写的属性,执行 convertForProperty,最终调用 TypeConverterDelegate#convertIfNecessary 进行处理。根据需要的属性类型,找到对应的编辑器,ResourceArrayPropertyEditor,执行 setValue。

  1. // ResourceArrayPropertyEditor
  2. @Override
  3. public void setValue(Object value) throws IllegalArgumentException {
  4. if (value instanceof Collection || (value instanceof Object[] && !(value instanceof Resource[]))) {
  5. Collection<?> input = (value instanceof Collection ? (Collection<?>) value : Arrays.asList((Object[]) value));
  6. Set<Resource> merged = new LinkedHashSet<>();
  7. for (Object element : input) {
  8. if (element instanceof String) {
  9. String pattern = resolvePath((String) element).trim();
  10. try {
  11. Resource[] resources = this.resourcePatternResolver.getResources(pattern);
  12. Collections.addAll(merged, resources);
  13. }
  14. catch (IOException ex) {
  15. ...
  16. }
  17. }
  18. else if (element instanceof Resource) {
  19. merged.add((Resource) element);
  20. }
  21. else {
  22. ...
  23. }
  24. }
  25. super.setValue(merged.toArray(new Resource[0]));
  26. }
  27. else {
  28. super.setValue(value);
  29. }
  30. }

遍历 locations 字符串数组,先执行 resolvePath,参考 spring 中对象创建之 BeanWrapperImpl 的初始化 可知,创建 ResourceArrayPropertyEditor 时,将 AbstractApplicationContext 作为参数 resourcePatternResolver,将 AbstractApplicationContext 中属性 environment 作为 propertyResolver,默认 ignoreUnresolvablePlaceholders 为 true。

  1. protected String resolvePath(String path) {
  2. if (this.propertyResolver == null) {
  3. this.propertyResolver = new StandardEnvironment();
  4. }
  5. return (this.ignoreUnresolvablePlaceholders ? this.propertyResolver.resolvePlaceholders(path) :
  6. this.propertyResolver.resolveRequiredPlaceholders(path));
  7. }

所以此处 propertyResolver 不为 null,调用 resolvePlaceholders 来解析 ${...},即宽松的解析策略。

接着调用 resourcePatternResolver.getResources,参考 spring 中的资源文件加载,此时会调用到 AbstractApplicationContext#getResources,继而调用 PathMatchingResourcePatternResolver#getResources。

对于 config.properties,执行 

new Resource[] {getResourceLoader().getResource(locationPattern)};

最终调用 DefaultResourceLoader#getResourceByPath,创建一个 ClassPathContextResource 实例对象。

此时对于非系统变量和环境变量对应的占位符,例如:${cc}.properties,此处无法解析,执行 resolvePath 会将原值返回,因为此处依然采用的是 AbstractApplicationContext 创建时的 environment,注册的 PropertySources 只有系统变量和环境变量,所以可替换的占位符很有限。接着又会调入 PathMatchingResourcePatternResolver#getResources,此时 AntPathMatcher#isPattern 将 {} 判断为路径匹配中的特殊字符,返回true,将其看作了需进行路径匹配的资源,调用 PathMatchingResourcePatternResolver#findPathMatchingResources。一番处理后 rootDirPath = "",subDirPath = ${cc}.properties,此时调用 getResources,会创建一个 path 为 "" 的 ClassPathContextResource,作为 rootDirResources 返回,接着对 rootDirResources 进行遍历,获取 rootDirUrl 为 当前 class 所在根目录,协议为 file,调用 PathMatchingResourcePatternResolver#doFindPathMatchingFileResources                             --> doFindMatchingFileSystemResources  --> retrieveMatchingFiles,一番检索匹配后,返回一个 Resource[0] 数组。

之后调用 super.setValue,对父类 java.beans.PropertyEditorSupport 中 value 进行赋值。接着又在 TypeConverterDelegate#doConvertValue 中获取到这个转换后的 value 进行返回。

initializeBean

AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization 中,此时由于已经注册 ApplicationContextAwareProcessor,会调用 ApplicationContextAwareProcessor#invokeAwareInterfaces,为 PropertySourcesPlaceholderConfigurer 设置 environment,为 AbstractApplicationContext 中属性 environment。

invokeBeanFactoryPostProcessors

实例对象创建完成之后,发起 PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors 调用。

  1. private static void invokeBeanFactoryPostProcessors(
  2. Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
  3. for (BeanFactoryPostProcessor postProcessor : postProcessors) {
  4. StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
  5. .tag("postProcessor", postProcessor::toString);
  6. postProcessor.postProcessBeanFactory(beanFactory);
  7. postProcessBeanFactory.end();
  8. }
  9. }

此时,就会调用 PropertySourcesPlaceholderConfigurer#postProcessBeanFactory。

  1. @Override
  2. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
  3. if (this.propertySources == null) {
  4. this.propertySources = new MutablePropertySources(); // 创建赋值
  5. if (this.environment != null) {
  6. PropertyResolver propertyResolver = this.environment;
  7. // 如果 ignoreUnresolvablePlaceholders 标志被设置为 true,我们必须创建一个本地 PropertyResolver 来强制执行该设置,
  8. // 因为 environment 很可能没有将 ignoreUnresolvablePlaceholders 设置为 true
  9. // See https://github.com/spring-projects/spring-framework/issues/27947
  10. // StandardEnvironment 是 ConfigurableEnvironment 实现类
  11. if (this.ignoreUnresolvablePlaceholders && (this.environment instanceof ConfigurableEnvironment)) {
  12. ConfigurableEnvironment configurableEnvironment = (ConfigurableEnvironment) this.environment;
  13. PropertySourcesPropertyResolver resolver =
  14. new PropertySourcesPropertyResolver(configurableEnvironment.getPropertySources());
  15. resolver.setIgnoreUnresolvableNestedPlaceholders(true);
  16. propertyResolver = resolver;
  17. }
  18. PropertyResolver propertyResolverToUse = propertyResolver;
  19. this.propertySources.addLast(
  20. new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
  21. @Override
  22. @Nullable
  23. public String getProperty(String key) {
  24. return propertyResolverToUse.getProperty(key);
  25. }
  26. }
  27. );
  28. }
  29. try {
  30. PropertySource<?> localPropertySource =
  31. new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
  32. // 默认不覆盖,addLast
  33. if (this.localOverride) {
  34. this.propertySources.addFirst(localPropertySource);
  35. }
  36. else {
  37. this.propertySources.addLast(localPropertySource);
  38. }
  39. }
  40. catch (IOException ex) {
  41. ...
  42. }
  43. }
  44. processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
  45. this.appliedPropertySources = this.propertySources;
  46. }

如果 PropertySourcesPlaceholderConfigurer 中 ignoreUnresolvablePlaceholders 被设置为 true,此时会创建一个 PropertySourcesPropertyResolver 实例对象,来代替 initializeBean 时设置的 environment 作为系统变量和环境变量下的属性解析器。否则,仍采用 environment。

接着执行 mergeProperties,加载配置的属性文件。

  1. protected Properties mergeProperties() throws IOException {
  2. // java.util.Properties
  3. Properties result = new Properties();
  4. if (this.localOverride) {
  5. // Load properties from file upfront, to let local properties override.
  6. loadProperties(result);
  7. }
  8. if (this.localProperties != null) {
  9. for (Properties localProp : this.localProperties) {
  10. CollectionUtils.mergePropertiesIntoMap(localProp, result);
  11. }
  12. }
  13. if (!this.localOverride) {
  14. // 加载 properties
  15. loadProperties(result);
  16. }
  17. return result;
  18. }
  19. // PropertiesLoaderSupport
  20. protected void loadProperties(Properties props) throws IOException {
  21. if (this.locations != null) {
  22. for (Resource location : this.locations) {
  23. try {
  24. PropertiesLoaderUtils.fillProperties(
  25. props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
  26. }
  27. catch (FileNotFoundException | UnknownHostException | SocketException ex) {
  28. ...
  29. }
  30. }
  31. }
  32. }
  33. // DefaultPropertiesPersister
  34. @Override
  35. public void load(Properties props, InputStream is) throws IOException {
  36. props.load(is);
  37. }

遍历 PropertySourcesPlaceholderConfigurer 对象创建时填充的属性 locations,加载 properties 文件,得到 Properties,之后将其包装为 PropertiesPropertySource 对象,加入 propertySources。

接着新建一个 PropertySourcesPropertyResolver,传入 propertySources,之后调用 PropertySourcesPlaceholderConfigurer#processProperties。

  1. protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
  2. final ConfigurablePropertyResolver propertyResolver) throws BeansException {
  3. // 利用新的 propertyResolver 来解析占位符
  4. propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
  5. propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
  6. propertyResolver.setValueSeparator(this.valueSeparator);
  7. // 根据 ignoreUnresolvablePlaceholders 来决定是否执行严格的占位符解析
  8. // 默认 false,即 resolveRequiredPlaceholders 执行严格的占位符解析
  9. StringValueResolver valueResolver = strVal -> {
  10. String resolved = (this.ignoreUnresolvablePlaceholders ?
  11. propertyResolver.resolvePlaceholders(strVal) :
  12. propertyResolver.resolveRequiredPlaceholders(strVal));
  13. if (this.trimValues) {
  14. resolved = resolved.trim();
  15. }
  16. return (resolved.equals(this.nullValue) ? null : resolved);
  17. };
  18. doProcessProperties(beanFactoryToProcess, valueResolver);
  19. }
  20. // PlaceholderConfigurerSupport
  21. protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
  22. StringValueResolver valueResolver) {
  23. BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
  24. String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
  25. for (String curName : beanNames) {
  26. // 不是当前 beanName,同一个 beanFactory
  27. if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
  28. BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
  29. try {
  30. visitor.visitBeanDefinition(bd);
  31. }
  32. catch (Exception ex) {
  33. ...
  34. }
  35. }
  36. }
  37. // 自 spring 2.5 开始,解析别名中的占位符
  38. beanFactoryToProcess.resolveAliases(valueResolver);
  39. // 自 spring 3.0 开始,为 beanFactory 添加 embeddedValueResolvers,解析注解属性中的占位符
  40. beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
  41. }
  42. // 访问替换 beanDefinition 中的占位符
  43. public void visitBeanDefinition(BeanDefinition beanDefinition) {
  44. // parentName
  45. visitParentName(beanDefinition);
  46. // beanClassName
  47. visitBeanClassName(beanDefinition);
  48. visitFactoryBeanName(beanDefinition);
  49. visitFactoryMethodName(beanDefinition);
  50. visitScope(beanDefinition);
  51. // PropertyValues
  52. if (beanDefinition.hasPropertyValues()) {
  53. visitPropertyValues(beanDefinition.getPropertyValues());
  54. }
  55. // ConstructorArgumentValues
  56. if (beanDefinition.hasConstructorArgumentValues()) {
  57. ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
  58. visitIndexedArgumentValues(cas.getIndexedArgumentValues());
  59. visitGenericArgumentValues(cas.getGenericArgumentValues());
  60. }
  61. }

利用 PropertySourcesPlaceholderConfigurer 相关属性来填充新建的 propertyResolver,接着创建一个 valueResolver,并将其封装为 BeanDefinitionVisitor,之后遍历 beanFactory 中注册的 BeanDefinition,利用 BeanDefinitionVisitor 逐个发起访问。

在 visitBeanDefinition 方法中,可以看到,分别对 parentName、beanClassName、factoryBeanName、factoryMethodName、scope、propertyValues、conStructorArgumentValues 进行访问,处理其中的占位符。

  1. @Nullable
  2. protected String resolveStringValue(String strVal) {
  3. if (this.valueResolver == null) {
  4. throw ...
  5. }
  6. String resolvedValue = this.valueResolver.resolveStringValue(strVal);
  7. // 未修改返回原值
  8. return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
  9. }

让前面创建的 valueResolver 进行处理,根据 PropertySourcesPlaceholderConfigurer 中 ignoreUnresolvablePlaceholders 的配置情况来决定选用哪种策略执行占位符解析,默认 false,即选用严格的解析策略,遇见不能解析的占位符直接抛出异常。

接下来就利用前面 PropertySourcesPropertyResolver 中介绍的解析办法,创建 strictHelper 进行解析,此时当再次调用 PropertySourcesPropertyResolver#getPropertyAsRawString 时,就会利用已加载的 PropertySource,获取占位符对应的属性。

还有一点需要注意,经此处解析后的 BeanDefinition 中相关字段和属性,在之后创建 bean 实例时就会使用解析后的值,而对于已经创建实例对象的 BeanDefinition,除非重新创建实例对象,否则已经创建的实例对象并不会采用解析后的值。

举个例子:

  1. <context:property-placeholder location="config.properties"/>
  2. <context:property-placeholder location="${cc}.properties"/>

config.properties 中配置 cc=a

通过前面的介绍可知,当调用 invokeBeanFactoryProcessors 方法时,已经完成了对两个 PropertySourcesPlaceholderConfigurer 实例对象的创建,并且针对第二个 PropertySourcesPlaceholderConfigurer 实例对象,在执行 applyPropertyValues 时,由于 ${cc}.properties 无法解析,将其处理成了一个长度为 0 的 Resource 数组,这样,就会出现 mergeProperties 时 config.properties 会正常加载,接着执行 visitBeanDefinition 时会将 第二个 PropertySourcesPlaceholderConfigurer 对应的 BeanDefinition 中 ${cc}.properties 替换为 a.properties,但由于第二个 PropertySourcesPlaceholderConfigurer 已经完成实例对象的创建,当第二个 PropertySourcesPlaceholderConfigurer 执行 mergeProperties 时,由于 locations 为 Resource[0],并不会进行实质的加载,当后续获取 a.properties 中配置的属性时,就会报错。

在 PlaceholderConfigurerSupport#doProcessProperties 方法中,当完成对 BeanDefinition 的访问之后,会对 SimpleAliasRegistry 中注册的 aliasMap 进行解析,aliasMap 中 key 和 value 都支持占位符解析。

接着将 valueResolver 添加到 AbstractBeanFactory 的 embeddedValueResolvers 集合中,用来在填充属性时,AutowiredAnnotationBeanPostProcessor#postProcessProperties 对 @Value 注解中占位符的解析。

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

闽ICP备14008679号