IDEA2023.1.3破解,IDEA破解,IDEA 2023.1破解,最新IDEA激活码

【spring源码分析】IOC容器初始化——查漏补缺(五)

IDEA2023.1.3破解,IDEA破解,IDEA 2023.1破解,最新IDEA激活码

前言:我们知道在Spring中经常使用配置文件的形式对进行属性的赋值,那配置文件的值是怎么赋值到属性上的呢,本文将对其进行分析。


首先了解一个类:PropertySourcesPlaceholderConfigurer,该类对程序中使用占位符的方式对属性进行赋值的形式进行解析,如在xml配置文件中进行了key-value的配置,在程序中使用该配置的值的形式。

108_1.png

分析:

从PropertySourcesPlaceholderConfigurer的继承结构图上可知,PropertySourcesPlaceholderConfigurer间接实现了Aware和BeanFactoryPostProcessor两大接口,这里只关注BeanFactoryPostProcessor(Aware接口前面已经分析了),BeanFactoryPostProcessor接口中就只有一个postProcessBeanFactory方法,其实现如下:

 // PropertySourcesPlaceholderConfigurer
 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
         // 如果propertySources为null,则初始化该属性
         if (this.propertySources == null) {
             this.propertySources = new MutablePropertySources();
             if (this.environment != null) {
                 this.propertySources.addLast(
                     new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
                         @Override
                         @Nullable
                         public String getProperty(String key) {
                             return this.source.getProperty(key);
                         }
                     }
                 );
             }
             try {
                 // 构建localPropertySource对象,Properties通过mergeProperties方法获取。
                 PropertySource<?> localPropertySource =
                         new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
                 if (this.localOverride) {
                     this.propertySources.addFirst(localPropertySource);
                 }
                 else {
                     this.propertySources.addLast(localPropertySource);
                 }
             }
             catch (IOException ex) {
                 throw new BeanInitializationException("Could not load properties", ex);
             }
         }
         // 进行占位符替换
         processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));
         this.appliedPropertySources = this.propertySources;
     }

分析:

  • 首先需要构建MutablePropertySources对象,用于存储Properties,注意Properties是通过mergeProperties获取。
  • 占位符的替换过程在processProperties函数中实现。

PropertiesLoaderSupport#mergeProperties

 protected Properties mergeProperties() throws IOException {
         // 创建Properties对象
         Properties result = new Properties();

         // 是否允许覆盖配置 之前
         if (this.localOverride) {
             // Load properties from file upfront, to let local properties override.
             loadProperties(result);
         }

         // 进行配置合并
         if (this.localProperties != null) {
             for (Properties localProp : this.localProperties) {
                 CollectionUtils.mergePropertiesIntoMap(localProp, result);
             }
         }
         // 再次进行覆盖 之后
         if (!this.localOverride) {
             // Load properties from file afterwards, to let those properties override.
             loadProperties(result);
         }

         return result;
     }

分析:

这里其实逻辑简单,主要通过loadProperties进行配置文件的导入。

PropertiesLoaderSupport#loadProperties

 protected void loadProperties(Properties props) throws IOException {
         // 遍历文件路径
         if (this.locations != null) {
             for (Resource location : this.locations) {
                 if (logger.isTraceEnabled()) {
                     logger.trace("Loading properties file from " + location);
                 }
                 try {
                     PropertiesLoaderUtils.fillProperties(
                             props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
                 } catch (FileNotFoundException | UnknownHostException ex) {
                     if (this.ignoreResourceNotFound) {
                         if (logger.isDebugEnabled()) {
                             logger.debug("Properties resource not found: " + ex.getMessage());
                         }
                     } else {
                         throw ex;
                     }
                 }
             }
         }
     }

分析:

这里代码逻辑简单,变量locations对象,然后通过PropertiesLoaderUtils#fillProperties方法进行属性填充,这里不对该方法进行分析,有兴趣的可以自己详细看下。

PropertiesLoaderSupport#processProperties

 protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
             final ConfigurablePropertyResolver propertyResolver) throws BeansException {
         // 设置"${"、"}"、":"
         propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
         propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
         propertyResolver.setValueSeparator(this.valueSeparator);

         // 根据是否需要忽略不能解析的符号进行分支处理
         StringValueResolver valueResolver = strVal -> {
             String resolved = (this.ignoreUnresolvablePlaceholders ?
                     propertyResolver.resolvePlaceholders(strVal) :
                     propertyResolver.resolveRequiredPlaceholders(strVal));
             if (this.trimValues) {
                 resolved = resolved.trim();
             }
             return (resolved.equals(this.nullValue) ? null : resolved);
         };

         // 进行具体解析
         doProcessProperties(beanFactoryToProcess, valueResolver);
     }

分析:

从函数的具体逻辑可以看出Spring中对占位符的解析只包含”${ }”和”:”。

  • 创建StringValueResolver对象,根据不同的策略(是否要忽略不能解析的符号),这里会在doProcessProperties中进行回调。其实resolvePlaceholders和resolveRequiredPlaceholders函数内部都调用的同一个函数,只是其初始化方式不同(false和true的区别),具体代码AbstractPropertyResolver中。
  • 最终的具体解析过程落在doProcessProperties函数中。

AbstractPropertyResolver#resolveRequiredPlaceholders

 public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
         // 如果strictHelper为null,则通过createPlaceholderHelper进行创建 
         // 注意这里createPlaceholderHelper入参为false,这就是和resolvePlaceholders不一样的地方
         if (this.strictHelper == null) {
             this.strictHelper = createPlaceholderHelper(false);
         }
         // 通过PropertyPlaceholderHelper进行解析
         return doResolvePlaceholders(text, this.strictHelper);
 }

 private PropertyPlaceholderHelper createPlaceholderHelper(boolean ignoreUnresolvablePlaceholders) {
         return new PropertyPlaceholderHelper(this.placeholderPrefix, this.placeholderSuffix,
                 this.valueSeparator, ignoreUnresolvablePlaceholders);
     }

 private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
         return helper.replacePlaceholders(text, this::getPropertyAsRawString);
     }
 // PropertyPlaceholderHelper
 public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
         Assert.notNull(value, "'value' must not be null");
         return parseStringValue(value, placeholderResolver, new HashSet<>());
     }

分析:

以上代码逻辑还是比较简单,最终的解析落入PropertyPlaceholderHelper#replacePlaceHolders函数中,该函数在【spring源码分析】IOC容器初始化(一)中已分析。

PlaceholderConfigurerSupport#doProcessProperties

 protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
             StringValueResolver valueResolver) {

         // 创建BeanDefinitionVisitor对象
         BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

         // 对BeanDefinition进行遍历
         String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
         for (String curName : beanNames) {
             // Check that we're not parsing our own bean definition,
             // to avoid failing on unresolvable placeholders in properties file locations.
             // 校验
             // 当前PlaceholderConfigurerSupport不在解析范围内  同一个Spring容器
             if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
                 BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
                 try {
                     // 访问bean,其实是对属性进行解析
                     visitor.visitBeanDefinition(bd);
                 }
                 catch (Exception ex) {
                     throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);
                 }
             }
         }

         // New in Spring 2.5: resolve placeholders in alias target names and aliases as well.
         // 别名占位符
         beanFactoryToProcess.resolveAliases(valueResolver);

         // New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.
         // 解析嵌入值的占位符,例如注释属性
         beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
     }

分析:

  • 首先创建BeanDefinitionVisitor对象,通过字符串解析器,用于后期对配置文件进行解析。
  • 对BeanDefinition进行遍历,通过visitBeanDefinition方法进行配置文件解析。
  • 解析别名占位符。
  • 解析嵌入值的占位符,例如注释属性。

其实整个方法的核心在于visitBeanDefinition方法。

BeanDefinitionVisitor#visitBeanDefinition

 public void visitBeanDefinition(BeanDefinition beanDefinition) {
         visitParentName(beanDefinition);
         visitBeanClassName(beanDefinition);
         visitFactoryBeanName(beanDefinition);
         visitFactoryMethodName(beanDefinition);
         visitScope(beanDefinition);
         if (beanDefinition.hasPropertyValues()) {
             visitPropertyValues(beanDefinition.getPropertyValues());
         }
         if (beanDefinition.hasConstructorArgumentValues()) {
             ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
             visitIndexedArgumentValues(cas.getIndexedArgumentValues());
             visitGenericArgumentValues(cas.getGenericArgumentValues());
         }
     }

分析:

该方法基本访问了BeanDefinition中所有值得访问的东西了,包括parent 、class 、factory-bean 、factory-method 、scope 、property 、constructor-arg。

这里关注visitPropertyValues方法。

BeanDefinitionVisitor#visitPropertyValues

     protected void visitPropertyValues(MutablePropertyValues pvs) {
         // 遍历PropertyValue数组
         PropertyValue[] pvArray = pvs.getPropertyValues();
         for (PropertyValue pv : pvArray) {
             // 解析真值
             Object newVal = resolveValue(pv.getValue());
             if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
                 // 设置到PropertyValue中
                 pvs.add(pv.getName(), newVal);
             }
         }
     }

分析:

该函数主要就是对属性数组进行遍历,通过resolveValue方法对属性进行解析获取最新值,如果新值与就只不相等,则用新值替换旧值。

BeanDefinitionVisitor#resolveValue

 protected Object resolveValue(@Nullable Object value) {
         if (value instanceof BeanDefinition) {
             visitBeanDefinition((BeanDefinition) value);
         }
         else if (value instanceof BeanDefinitionHolder) {
             visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
         }
         else if (value instanceof RuntimeBeanReference) {
             RuntimeBeanReference ref = (RuntimeBeanReference) value;
             String newBeanName = resolveStringValue(ref.getBeanName());
             if (newBeanName == null) {
                 return null;
             }
             if (!newBeanName.equals(ref.getBeanName())) {
                 return new RuntimeBeanReference(newBeanName);
             }
         }
         else if (value instanceof RuntimeBeanNameReference) {
             RuntimeBeanNameReference ref = (RuntimeBeanNameReference) value;
             String newBeanName = resolveStringValue(ref.getBeanName());
             if (newBeanName == null) {
                 return null;
             }
             if (!newBeanName.equals(ref.getBeanName())) {
                 return new RuntimeBeanNameReference(newBeanName);
             }
         }
         else if (value instanceof Object[]) {
             visitArray((Object[]) value);
         }
         else if (value instanceof List) {
             visitList((List) value);
         }
         else if (value instanceof Set) {
             visitSet((Set) value);
         }
         else if (value instanceof Map) {
             visitMap((Map) value);
         }
         else if (value instanceof TypedStringValue) {
             TypedStringValue typedStringValue = (TypedStringValue) value;
             String stringValue = typedStringValue.getValue();
             if (stringValue != null) {
                 String visitedString = resolveStringValue(stringValue);
                 typedStringValue.setValue(visitedString);
             }
         }
         // 由于Properties中的是String,所以重点在此
         else if (value instanceof String) {
             return resolveStringValue((String) value);
         }
         return value;
     }

分析:

该函数对各种类型的属性进行解析,由于配置为String类型的,因此这里关注resolveStringValue函数。

BeanDefinitionVisitor#resolveStringValue

 protected String resolveStringValue(String strVal) {
         if (this.valueResolver == null) {
             throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +
                     "object into the constructor or override the 'resolveStringValue' method");
         }
         // 解析真值
         String resolvedValue = this.valueResolver.resolveStringValue(strVal);
         // Return original String if not modified.
         return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
     }

分析:

这里具体的解析就会回调valueResolver(根据不同策略创建的StringValueResolver对象),然后进入具体的解析,其解析过程已经分析,这里不在赘述。

总结

至此关于占位符的解析过程就大致分析完了,其实里面还有很多值得我们细究的地方,具体过程可debug调试一遍,可能会有更深的理解。


by Shawn Chen,2019.05.08,晚。

出处:https://www.cnblogs.com/developer_chan/category/1347173.html

文章永久链接:https://tech.souyunku.com/?p=13700


Warning: A non-numeric value encountered in /data/wangzhan/tech.souyunku.com.wp/wp-content/themes/dux/functions-theme.php on line 1154
赞(96) 打赏



未经允许不得转载:搜云库技术团队 » 【spring源码分析】IOC容器初始化——查漏补缺(五)

IDEA2023.1.3破解,IDEA破解,IDEA 2023.1破解,最新IDEA激活码
IDEA2023.1.3破解,IDEA破解,IDEA 2023.1破解,最新IDEA激活码

评论 抢沙发

大前端WP主题 更专业 更方便

联系我们联系我们

觉得文章有用就打赏一下文章作者

微信扫一扫打赏

微信扫一扫打赏


Fatal error: Uncaught Exception: Cache directory not writable. Comet Cache needs this directory please: `/data/wangzhan/tech.souyunku.com.wp/wp-content/cache/comet-cache/cache/https/tech-souyunku-com/index.q`. Set permissions to `755` or higher; `777` might be needed in some cases. in /data/wangzhan/tech.souyunku.com.wp/wp-content/plugins/comet-cache/src/includes/traits/Ac/ObUtils.php:367 Stack trace: #0 [internal function]: WebSharks\CometCache\Classes\AdvancedCache->outputBufferCallbackHandler() #1 /data/wangzhan/tech.souyunku.com.wp/wp-includes/functions.php(5109): ob_end_flush() #2 /data/wangzhan/tech.souyunku.com.wp/wp-includes/class-wp-hook.php(303): wp_ob_end_flush_all() #3 /data/wangzhan/tech.souyunku.com.wp/wp-includes/class-wp-hook.php(327): WP_Hook->apply_filters() #4 /data/wangzhan/tech.souyunku.com.wp/wp-includes/plugin.php(470): WP_Hook->do_action() #5 /data/wangzhan/tech.souyunku.com.wp/wp-includes/load.php(1097): do_action() #6 [internal function]: shutdown_action_hook() #7 {main} thrown in /data/wangzhan/tech.souyunku.com.wp/wp-content/plugins/comet-cache/src/includes/traits/Ac/ObUtils.php on line 367