spring boot 源码解析13-@ConfigurationProperties是如何生效的

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

前言

新的一年到了,在这里先祝大家新年快乐.我们在上一篇spring boot 源码解析12-servlet容器的建立 中 分析 ServerProperties时,发现其类上有@ConfigurationProperties 注解,加上该注解后,就会注入在application.properties中server开头的属性,那么它是怎么生效的呢?我们这篇文章就来分析一下.这篇文章内容比较长,大家慢慢看…

@ConfigurationProperties 使用方式

  1. 我们首先声明实体类,用于属性的注入.代码如下:

        public class People {
    
            private String name;
    
            private Integer age;
    
            private List address;
    
            private Phone phone;
    
            // get set 忽略,自己加上即可..
        }
    
        public class Phone {
    
            private String number;
    
            // get set 忽略,自己加上即可.. 
    
        }
  2. 在application.properties 中加入如下配置:

        com.example.demo.name=${aaa:hi}
        com.example.demo.age=11
        com.example.demo.address[0]=北京
        com.example.demo.address[1]=上海
        com.example.demo.address[2]=广州
        com.example.demo.phone.number=1111111
  3. @ConfigurationProperties 注解支持两种方式.

    1. 加在类上,需要和@Component注解,结合使用.代码如下:

          @Component
          @ConfigurationProperties(prefix = "com.example.demo")
          public class People {
      
              private String name;
      
              private Integer age;
      
              private List address;
      
              private Phone phone;
          }   
    2. 通过@Bean的方式进行声明,这里我们加在启动类即可,代码如下:

          @SpringBootApplication
          public class DemoApplication {
      
              @Bean
              @ConfigurationProperties(prefix = "com.example.demo")
              public People people() {
      
                  return new People();
              }
      
              public static void main(String[] args) {
                  SpringApplication.run(DemoApplication.class, args);
              }
      
          }

    这里我们使用第2种,原因是这样好debug,看源码…

  4. 我们再写一个Controller进行测试一下吧.代码如下:

        @RestController
        public class TestController {
    
            @Autowired
            private People people;
    
            @RequestMapping("/get_name")
            public String getName() {
    
                return people.getName();
            }
    
            @RequestMapping("/get_address")
            public List getAddress() {
    
                return people.getAddress();
            }
    
            @RequestMapping("/get_phone_numer")
            public String getNumber() {
    
                return people.getPhone().getNumber();
            }
        }

    访问 /get_name,其返回值如下:

    hi

    访问 /get_address,其返回值如下:

    [“北京”,”上海”,”广州”]

    访问 get_phone_numer,其返回值如下:

    1111111

    使用方式就介绍完了,接下来,我们就来看看spring 是如何处理的吧.

解析

我们应该知道了@ConfigurationProperties 和 @Bean 或者 @Component 等只要能生成spring bean 的注解 结合起来使用,这样的话,当其他类注入该类时,就会触发该类的加载过程,那么在加载过程中,会调用AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitialization.因此会触发ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization的调用,这里就是我们的起点.

  1. ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization 代码如下:

        public Object postProcessBeforeInitialization(Object bean, String beanName)
                throws BeansException {
            // 1. 获得类上的@ConfigurationProperties注解,如果注解存在,则调用postProcessBeforeInitialization 进行处理
            ConfigurationProperties annotation = AnnotationUtils
                    .findAnnotation(bean.getClass(), ConfigurationProperties.class);
            if (annotation != null) {
                postProcessBeforeInitialization(bean, beanName, annotation);
            }
            // 2. 寻找工厂方法上是否有@ConfigurationProperties 注解,如果存在的话,则调用postProcessBeforeInitialization进行处理
            annotation = this.beans.findFactoryAnnotation(beanName,
                    ConfigurationProperties.class);
            if (annotation != null) {
                postProcessBeforeInitialization(bean, beanName, annotation);
            }
            return bean;
        }

    2件事:

    1. 获得类上的@ConfigurationProperties注解,如果注解存在,则调用postProcessBeforeInitialization 进行处理
    2. 寻找工厂方法上是否有@ConfigurationProperties 注解,如果存在的话,则调用postProcessBeforeInitialization进行处理.对应的是@Bean的方式.
  2. 不管怎么样,最终都会调用ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization.代码如下:

        private void postProcessBeforeInitialization(Object bean, String beanName,
                ConfigurationProperties annotation) {
            Object target = bean;
            // 1. 实例化PropertiesConfigurationFactory,该类实现了FactoryBean, MessageSourceAware, InitializingBean 接口,并进行一些属性的设置
            PropertiesConfigurationFactory factory = new PropertiesConfigurationFactory(
                    target);
            factory.setPropertySources(this.propertySources);
            factory.setValidator(determineValidator(bean));
            // If no explicit conversion service is provided we add one so that (at least)
            // comma-separated arrays of convertibles can be bound automatically
            // 由于conversionService 一直为 null,因此会调用getDefaultConversionService
            factory.setConversionService(this.conversionService == null
                    ? getDefaultConversionService() : this.conversionService);
            if (annotation != null) {
                // 2. 如果注解存在,这是肯定的,不然也不会执行该方法,则根据@ConfigurationProperties的值进行配置
                factory.setIgnoreInvalidFields(annotation.ignoreInvalidFields());
                factory.setIgnoreUnknownFields(annotation.ignoreUnknownFields());
                factory.setExceptionIfInvalid(annotation.exceptionIfInvalid());
                factory.setIgnoreNestedProperties(annotation.ignoreNestedProperties());
                if (StringUtils.hasLength(annotation.prefix())) {
                    // 2.1 如果配置了prefix,或者value 值,则设置TargetName
                    factory.setTargetName(annotation.prefix());
                }
            }
            try {
                // 3. 进行绑定
                factory.bindPropertiesToTarget();
            }
            catch (Exception ex) {
                String targetClass = ClassUtils.getShortName(target.getClass());
                throw new BeanCreationException(beanName, "Could not bind properties to "
                        + targetClass + " (" + getAnnotationDetails(annotation) + ")", ex);
            }
        }
    

    3件事:

    1. 实例化PropertiesConfigurationFactory,该类实现了FactoryBean, MessageSourceAware, InitializingBean 接口,并进行一些属性的设置.

      1. 将ConfigurationPropertiesBindingPostProcessor中的propertySources赋值给PropertiesConfigurationFactory
      2. 通过调用determineValidator方法,生成Validator,并进行赋值.代码如下:

            private Validator determineValidator(Object bean) {
            // 1. 获得validator
            Validator validator = getValidator();
            // 2. 如果validator不等于null并且该Validator 支持该bean的话
            boolean supportsBean = (validator != null && validator.supports(bean.getClass()));
            if (ClassUtils.isAssignable(Validator.class, bean.getClass())) {// 3 如果当前类为Validator的子类
                // 3.1 如果supportsBean,则实例化ChainingValidator
                if (supportsBean) {
                    return new ChainingValidator(validator, (Validator) bean);
                }
                // 3.2 否则强转为Validator
                return (Validator) bean;
            }
            // 4. 最后,如果supportsBean 则 返回Validator 否则 返回null
            return (supportsBean ? validator : null);
            }

        4件事:

        1. 调用getValidator方法获得Validator.代码如下:

              private Validator getValidator() {
              // 1. 由之前可知,该validator 一直都是null.
              if (this.validator != null) {
              return this.validator;
              }
              // 2. 如果localValidator 等于null并且是jsr303环境的话,则实例化ValidatedLocalValidatorFactoryBean,并赋值给localValidator,lazy-init
              // ValidatedLocalValidatorFactoryBean 实现了ValidatorFactory, ApplicationContextAware, InitializingBean, DisposableBean,SmartValidator, javax.validation.Validator
              if (this.localValidator == null && isJsr303Present()) {
              this.localValidator = new ValidatedLocalValidatorFactoryBean(
                      this.applicationContext);
              }
              return this.localValidator;
              }
          1. 如果validator 不等于null,则直接返回.可是该validator是一直等于null.原因如下:
            ConfigurationPropertiesBindingPostProcessor 实现了InitializingBean接口,因此为调用其afterPropertiesSet方法,在该方法,有如下片段:

                if (this.validator == null) {
                // 2. 尝试获得id 为 configurationPropertiesValidator,type为Validator 的bean,此时是没有获取到
                this.validator = getOptionalBean(VALIDATOR_BEAN_NAME, Validator.class);
                }

            会尝试从beanFactory中获得id 为 configurationPropertiesValidator,type 为 Validator的bean,可是默认情况下,是不存在的.

          2. 如果localValidator 等于null并且是jsr303环境的话,则实例化ValidatedLocalValidatorFactoryBean,并赋值给localValidator,这是一个lazy-init,ValidatedLocalValidatorFactoryBean 实现了ValidatorFactory, ApplicationContextAware, InitializingBean, DisposableBean,SmartValidator, javax.validation.Validator接口.
          3. 如果不等于null,则直接返回
        2. 如果validator不等于null并且该Validator 支持该bean的话,则supportsBean等于true,否则为false.
        3. 如果当前类为Validator的子类

          1. 如果supportsBean为true,则实例化ChainingValidator,则初始化ChainingValidator.进行返回
          2. 否则强转为Validator,进行返回
        4. 最后,如果supportsBean 则 返回Validator 否则 返回null
      3. 如果ConfigurationPropertiesBindingPostProcessor#conversionService 等于null,则调用getDefaultConversionService获得默认的ConversionService.否则,直接将本类的conversionService 赋值给PropertiesConfigurationFactory 的ConversionService.还是由于conversionService一直为 null,因此会调用getDefaultConversionService.代码如下:

                private ConversionService getDefaultConversionService() {
            // 又是lazy-init 风格
            // 1. 如果defaultConversionService 等于null,则意味着是第一次调用
            if (this.defaultConversionService == null) {
                // 1.1 实例化DefaultConversionService
                DefaultConversionService conversionService = new DefaultConversionService();
                // 1.2 调用autowireBean进行注入依赖,此时会注入converters,genericConverters
                this.applicationContext.getAutowireCapableBeanFactory().autowireBean(this);
                // 1.3 遍历converters,genericConverters 依次加入到conversionService的converters中
                for (Converter converter : this.converters) {
                    conversionService.addConverter(converter);
                }
                for (GenericConverter genericConverter : this.genericConverters) {
                    conversionService.addConverter(genericConverter);
                }
                // 1.4 赋值给defaultConversionService
                this.defaultConversionService = conversionService;
            }
            // 2. 如果不等于null,则直接返回
            return this.defaultConversionService;
            }

        2件事:

        1. 如果defaultConversionService 等于null,则意味着是第一次调用,又是lazy-init 风格.

          1. 实例化DefaultConversionService
          2. 调用autowireBean进行注入依赖,此时会注入converters,genericConverters
          3. 遍历converters,genericConverters 依次加入到conversionService的converters中
          4. 赋值给defaultConversionService
        2. 如果不等于null,则直接返回
    2. 如果注解存在,这是肯定的,不然也不会执行该方法,则根据@ConfigurationProperties的值进行配置

      1. 如果配置了prefix,或者value 值,则设置TargetName.这个后面解析的时候会用到该值.
    3. 调用PropertiesConfigurationFactory#bindPropertiesToTarget,进行绑定
  3. PropertiesConfigurationFactory#bindPropertiesToTarget 代码如下:

        public void bindPropertiesToTarget() throws BindException {
            // 1.首先判断propertySources是否为null,如果为null的话,抛出异常.一般不会为null的
            Assert.state(this.propertySources != null, "PropertySources should not be null");
            try {
                if (logger.isTraceEnabled()) {
                    logger.trace("Property Sources: " + this.propertySources);
    
                }
                // 2. 将hasBeenBound 设为true
                this.hasBeenBound = true;
                // 3. 调用doBindPropertiesToTarget
                doBindPropertiesToTarget();
            }
            catch (BindException ex) {
                if (this.exceptionIfInvalid) {
                    throw ex;
                }
                PropertiesConfigurationFactory.logger
                        .error("Failed to load Properties validation bean. "
                                + "Your Properties may be invalid.", ex);
            }
        }

    3件事:

    1. 首先判断propertySources是否为null,如果为null的话,抛出异常.一般不会为null的.因为该类在实例化的时候,已经对其进行赋值了
    2. 将hasBeenBound 设为true
    3. 调用doBindPropertiesToTarget.代码如下:

              private void doBindPropertiesToTarget() throws BindException {
          // 1. 初始化RelaxedDataBinder 并进行设置一下属性. // target = SpringApplication.这样RelaxedDataBinder也就持有了SpringApplication
          RelaxedDataBinder dataBinder = (this.targetName != null
                  ? new RelaxedDataBinder(this.target, this.targetName)
                  : new RelaxedDataBinder(this.target));
          // 对于当前场景来说validator还是为null的,在 ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization 中,该validator为ValidatedLocalValidatorFactoryBean
          if (this.validator != null
                  && this.validator.supports(dataBinder.getTarget().getClass())) {
              dataBinder.setValidator(this.validator);
          }
          if (this.conversionService != null) {
              // 持有了一系列的转换器
              dataBinder.setConversionService(this.conversionService);
          }
          dataBinder.setAutoGrowCollectionLimit(Integer.MAX_VALUE);
          dataBinder.setIgnoreNestedProperties(this.ignoreNestedProperties);
          dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
          dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
          // 2. 扩展点,空实现
          customizeBinder(dataBinder);
          // 3. 获得relaxedTargetNames,对于当前来说,其值为-->spring.main,也就是获得@ConfigurationProperties中配置的prefix
          Iterable relaxedTargetNames = getRelaxedTargetNames();
          // 4. 通过遍历target的属性,这里的target为SpringApplication.然后将SpringApplication的属性按照单词划分的规则,与relaxedTargetNames进行拼接
          // 举例说明:SpringApplication中有一个logStartupInfo属性,则拆分为log-startup-info,然后与spring.main拼接为
          // spring.main.log-startup-info 和 spring.main_log-startup-info
          // 通过拼接生成key
          Set names = getNames(relaxedTargetNames);
          // 5. 生成PropertyValues,此时就已经将配置文件中的占位符解析完了
          PropertyValues propertyValues = getPropertySourcesPropertyValues(names,
                  relaxedTargetNames);
          // 6. 调用bind,进行绑定
          dataBinder.bind(propertyValues);
          if (this.validator != null) {
              dataBinder.validate();
          }
          // 7. 检查在绑定过程中是否出现异常,如果有的话,抛出BindException
          checkForBindingErrors(dataBinder);
          }

      7件事:

      1. 初始化RelaxedDataBinder 并进行设置一下属性,target = People.这样RelaxedDataBinder也就持有了People.

        1. 如果validator不等于null,并且validator支持该类型的话,则设置RelaxedDataBinder的Validator,对于当前场景来说,是validator.
        2. 如果conversionService 不等于null,则设置ConversionService,这样RelaxedDataBinder就持有了一系列的转换器
        3. 设置AutoGrowCollectionLimit 为Integer.MAX_VALUE,该属性在处理集合属性注入时会用到
        4. 设置是否忽略嵌套属性,默认是false.
        5. 设置是否忽略不正确的属性,默认是false
        6. 设置是否忽略未知的子弹,默认是true
      2. 调用customizeBinder,这个扩展点,默认是空实现
      3. 调用getRelaxedTargetNames,对于当前来说,其值为–>com.example.demo,也就是获得@ConfigurationProperties中配置的prefix
      4. 通过遍历target的属性,这里的target为People.然后将People的属性按照单词划分的规则,与relaxedTargetNames进行拼接.举例说明: People中有一个name属性,则拆分后为name,然后与com.example.demo拼接为com.example.demo.name
      5. 调用getPropertySourcesPropertyValues,生成PropertyValues,在这步完成了占位符解析.这个步骤很关键,我们在第4点中进行分析.
      6. 调用bind,进行绑定
      7. 检查在绑定过程中是否出现异常,如果有的话,抛出BindException
  4. getPropertySourcesPropertyValues.代码如下:

            private PropertyValues getPropertySourcesPropertyValues(Set names,
                Iterable relaxedTargetNames) {
            // 1. 根据names和relaxedTargetNames 生成PropertyNamePatternsMatcher
            PropertyNamePatternsMatcher includes = getPropertyNamePatternsMatcher(names,
                    relaxedTargetNames);
            // 2. 返回PropertySourcesPropertyValues
            return new PropertySourcesPropertyValues(this.propertySources, names, includes,
                    this.resolvePlaceholders);
        }
    1. 根据names和relaxedTargetNames 生成PropertyNamePatternsMatcher.代码如下:

          private PropertyNamePatternsMatcher getPropertyNamePatternsMatcher(Set names,
              Iterable relaxedTargetNames) {
          // 1. 如果ignoreUnknownFields 并且 target 不是map的子类,则返回DefaultPropertyNamePatternsMatcher,在@ConfigurationProperties中,ignoreUnknownFields默认是true
          if (this.ignoreUnknownFields && !isMapTarget()) {
              // Since unknown fields are ignored we can filter them out early to save
              // unnecessary calls to the PropertySource.
              return new DefaultPropertyNamePatternsMatcher(EXACT_DELIMITERS, true, names);
          }
          // 2. 如果relaxedTargetNames 不等于null,则通过对relaxedTargetNames去重后,返回DefaultPropertyNamePatternsMatcher
          if (relaxedTargetNames != null) {
              // We can filter properties to those starting with the target name, but
              // we can't do a complete filter since we need to trigger the
              // unknown fields check
              Set relaxedNames = new HashSet();
              for (String relaxedTargetName : relaxedTargetNames) {
                  relaxedNames.add(relaxedTargetName);
              }
              return new DefaultPropertyNamePatternsMatcher(TARGET_NAME_DELIMITERS, true,
                      relaxedNames);
          }
          // Not ideal, we basically can't filter anything
          // 3. 返回默认的
          return PropertyNamePatternsMatcher.ALL;
          }
      

      3件事:

      1. 如果ignoreUnknownFields 并且 target 不是map的子类,则返回DefaultPropertyNamePatternsMatcher,在@ConfigurationProperties中,ignoreUnknownFields默认是true.在此时,由于target 为People,因此返回DefaultPropertyNamePatternsMatcher.
      2. 如果relaxedTargetNames 不等于null,则通过对relaxedTargetNames去重后,返回DefaultPropertyNamePatternsMatcher
      3. 返回默认的
    2. 返回PropertySourcesPropertyValues.其类图如下:

      20191017100469\_1.png

      其构造器如下:

          PropertySourcesPropertyValues(PropertySources propertySources,
              Collection nonEnumerableFallbackNames,
              PropertyNamePatternsMatcher includes, boolean resolvePlaceholders) {
          Assert.notNull(propertySources, "PropertySources must not be null");
          Assert.notNull(includes, "Includes must not be null");
          this.propertySources = propertySources;
          this.nonEnumerableFallbackNames = nonEnumerableFallbackNames;
          this.includes = includes;
          this.resolvePlaceholders = resolvePlaceholders;
          PropertySourcesPropertyResolver resolver = new PropertySourcesPropertyResolver(
                  propertySources);
          for (PropertySource source : propertySources) {
              processPropertySource(source, resolver);
          }
          }

      3件事:

      1. 属性赋值.
      2. 实例化PropertySourcesPropertyResolver
      3. 遍历propertySources,依次调用processPropertySource.代码如下:

                private void processPropertySource(PropertySource source,
            PropertySourcesPropertyResolver resolver) {
            if (source instanceof CompositePropertySource) {
            processCompositePropertySource((CompositePropertySource) source, resolver);
            }
            else if (source instanceof EnumerablePropertySource) {
            processEnumerablePropertySource((EnumerablePropertySource) source,
                    resolver, this.includes);
            }
            else {
            processNonEnumerablePropertySource(source, resolver);
            }
            }
        1. 如果PropertySource是CompositePropertySource的子类,则调用processCompositePropertySource方法,而该方法最终还是调用了processPropertySource,做递归处理.
        2. 如果PropertySource是EnumerablePropertySource的子类,则调用processEnumerablePropertySource.这里需要说明一下,我们是配置在application.properties中,那么其PropertySource 为 PropertiesPropertySource,是EnumerablePropertySource的子类,其继承结构如下:

          20191017100469\_2.png
          因此,关于配置文件属性的注入,最终会在这里执行.

        3. 否则,调用processNonEnumerablePropertySource.

        我们重点来看processEnumerablePropertySource,代码如下:

            private void processEnumerablePropertySource(EnumerablePropertySource source,
            PropertySourcesPropertyResolver resolver,
            PropertyNamePatternsMatcher includes) {
            if (source.getPropertyNames().length > 0) {
            for (String propertyName : source.getPropertyNames()) {
                if (includes.matches(propertyName)) {// 如果存在的话,则加入到propertyValues中
                    Object value = getEnumerableProperty(source, resolver, propertyName);
                    putIfAbsent(propertyName, value, source);
                }
            }
            }
            }

        思路很简单,

        1. 首先判断source中是否有属性的配置,如果有的话,则依次遍历之
        2. 在遍历过程中,会调用PropertyNamePatternsMatcher#matches 判断是否匹配.这里说明一下,这里使用的是DefaultPropertyNamePatternsMatcher,其matches 会依次遍历其内部的names 看是否与传入的propertyName 匹配,这里的names 就是在实例化时传入的com.example.demo.name等之类的东西.

          1. 如果匹配的话,则调用getEnumerableProperty 获得值.代码如下:

                private Object getEnumerableProperty(EnumerablePropertySource source,
                PropertySourcesPropertyResolver resolver, String propertyName) {
                try {
                if (this.resolvePlaceholders) {
                    return resolver.getProperty(propertyName, Object.class);
                }
                }
                catch (RuntimeException ex) {
                // Probably could not resolve placeholders, ignore it here
                }
                return source.getProperty(propertyName);
                }
            1. 如果resolvePlaceholders 为true,则调用PropertySourcesPropertyResolver#getProperty 处理,由于resolvePlaceholders 默认为true,因此一般都会执行这里.
            2. 否则,直接从EnumerablePropertySource 获取值即可.
          2. 调用putIfAbsent 将值,属性名,保存到propertyValues 中.

          其中2.1 会调用如下代码:

              public  T getProperty(String key, Class targetValueType) {
              return getProperty(key, targetValueType, true);
                      }

          最终调用如下代码:

              protected  T getProperty(String key, Class targetValueType, boolean resolveNestedPlaceholders) {
              if (this.propertySources != null) {
              for (PropertySource propertySource : this.propertySources) {
              if (logger.isTraceEnabled()) {
              logger.trace("Searching for key '" + key + "' in PropertySource '" +
                      propertySource.getName() + "'");
              }
              Object value = propertySource.getProperty(key);
              if (value != null) {
              if (resolveNestedPlaceholders && value instanceof String) {
                  value = resolveNestedPlaceholders((String) value);
              }
              logKeyFound(key, propertySource, value);
              return convertValueIfNecessary(value, targetValueType);
              }
              }
              }
              if (logger.isDebugEnabled()) {
              logger.debug("Could not find key '" + key + "' in any property source");
              }
              return null;
              }
          

          3件事

          1. 如果propertySources 不等于null,则依次遍历propertySources,进行处理

            1. 通过调用PropertySource#getProperty进行获取

              1. 如果获取到值的话,

                1. 如果resolveNestedPlaceholders(这个一般都是true) 并且value 为String,则调用resolveNestedPlaceholders处理占位符–>${},一般这个步骤都会执行的.
                2. 打印日志
                3. 尝试对其进行转换.
          2. 如果经过第1步处理,还是没找到的话,则直接返回null

          1.1.1.1 最终会调用如下方法.代码如下:

                  public String resolvePlaceholders(String text) {
              if (this.nonStrictHelper == null) {
              this.nonStrictHelper = createPlaceholderHelper(true);
              }
              return doResolvePlaceholders(text, this.nonStrictHelper);
              }
          1. 如果nonStrictHelper等于null,则调用createPlaceholderHelper进行实例化.lazy-init 风格.
          2. 调用AbstractPropertyResolver#doResolvePlaceholders.代码如下:

                private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
                return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
                @Override
                public String resolvePlaceholder(String placeholderName) {
                return getPropertyAsRawString(placeholderName);
                }
                });
                }

            这里直接调用了第一步实例化的PropertyPlaceholderHelper的replacePlaceholders进行处理,同时实例化了一个PlaceholderResolver,该类在获取值的时候会用到,这个后面会有介绍.PropertyPlaceholderHelper#replacePlaceholders 代码如下:

                public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
                Assert.notNull(value, "'value' must not be null");
                return parseStringValue(value, placeholderResolver, new HashSet());
                }

            最终调用如下代码:

                protected String parseStringValue(
                String value, PlaceholderResolver placeholderResolver, Set visitedPlaceholders) {
                StringBuilder result = new StringBuilder(value);
                // 1. 通过String#indexOf 获取前缀(一般都是${)的下标
                int startIndex = value.indexOf(this.placeholderPrefix);
                // 2 如果存在
                while (startIndex != -1) {
                // 2.1 获得后缀,此时获得是最小的后缀,嵌套处理
                int endIndex = findPlaceholderEndIndex(result, startIndex);
                if (endIndex != -1) {// 3 如果endIndex 存在,
                // 3.1 通过字符串的截取获得占位符,如${placeholder},那么此时获得的是placeholder
                String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
                String originalPlaceholder = placeholder;
                // 3.2 进行循环引用的检查,如果存在,则抛出IllegalArgumentException
                if (!visitedPlaceholders.add(originalPlaceholder)) {
                throw new IllegalArgumentException(
                        "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
                }
                // Recursive invocation, parsing placeholders contained in the placeholder key.
                // 3.3 递归处理
                placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
                // Now obtain the value for the fully resolved key...
                // 3.4 进行解析占位符
                String propVal = placeholderResolver.resolvePlaceholder(placeholder);
                // 3.5 如果propVal 不等于null并且 valueSeparator(默认为 :)不等于null,则此时意味有默认值,
                // 那么此时调用placeholderResolver#resolvePlaceholder 进行解析,如果解析失败的话,则返回默认值
                if (propVal == null && this.valueSeparator != null) {
                int separatorIndex = placeholder.indexOf(this.valueSeparator);
                if (separatorIndex != -1) {
                    String actualPlaceholder = placeholder.substring(0, separatorIndex);
                    String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                    propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                    if (propVal == null) {
                        propVal = defaultValue;
                    }
                }
                }
                // 3.6
                if (propVal != null) {
                // Recursive invocation, parsing placeholders contained in the
                // previously resolved placeholder value.
                // 3.6.1 如果propVal 不等于null,则意味着解析成功,则继续递归处理,处理完后,进行替换,
            
                propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                // 进行替换
                result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                if (logger.isTraceEnabled()) {
                    logger.trace("Resolved placeholder '" + placeholder + "'");
                }
                // 重新计算startIndex
                startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
                }
                else if (this.ignoreUnresolvablePlaceholders) {
                // Proceed with unprocessed value.
                // 3.6.2 如果没有解析成功并且ignoreUnresolvablePlaceholders,则重新计算startIndex
                startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
                }
                else {
                // 3.6.3 抛出IllegalArgumentException
                throw new IllegalArgumentException("Could not resolve placeholder '" +
                        placeholder + "'" + " in value \"" + value + "\"");
                }
                // 3.7 从visitedPlaceholders 删除.该算法有点类似dfs.
                visitedPlaceholders.remove(originalPlaceholder);
                }
                else {
                // 2.2 将startIndex 设为-1,则意味着已经处理完了
                startIndex = -1;
                }
                }
                return result.toString();
                }

            3件事:

            1. 通过String#indexOf 获取前缀(一般都是${)的下标
            2. 如果存在

              1. 获得后缀,此时获得是最小的后缀,嵌套处理
              2. 如果endIndex 存在,

                1. 通过字符串的截取获得占位符,如${placeholder},那么此时获得的是placeholder
                2. 进行循环引用的检查,如果存在,则抛出IllegalArgumentException
                3. 调用parseStringValue,进行递归处理.
                4. 调用PlaceholderResolver#resolvePlaceholder进行解析占位符
                5. 如果propVal 不等于null并且 valueSeparator(默认为 :)不等于null,则此时意味有默认值,那么此时调用placeholderResolver#resolvePlaceholder 进行解析,如果解析失败的话,则返回默认值
                6. 如果propVal 不等于null,则意味着解析成功,则继续递归处理,处理完后,进行替换,重新计算startIndex
                7. 如果没有解析成功并且ignoreUnresolvablePlaceholders,则重新计算startIndex
                8. 其他情况下,则抛出异常
                9. 从visitedPlaceholders 删除.该算法有点类似dfs.
            3. 如果不存在,则将startIndex 设为-1,则意味着已经处理完了.

            关于占位符的处理,集合,对象导航,属性转换的处理,我们这里先不解析,我们先解析最简单的情况,为People注入name 属性.并且配置文件中的配置如下:

            com.example.demo.name=hi

  5. 视线回到PropertiesConfigurationFactory#doBindPropertiesToTarget中来,此时执行完了getPropertySourcesPropertyValues,接下来就该执行第6步,调用DataBinder#bind进行绑定.这里还是假设我们只配置了com.example.demo.name=hi. 代码如下:

        public void bind(PropertyValues pvs) {
            MutablePropertyValues mpvs = (pvs instanceof MutablePropertyValues) ?
                    (MutablePropertyValues) pvs : new MutablePropertyValues(pvs);
            doBind(mpvs);
        }

    调用DataBinder#doBind,代码如下:

        protected void doBind(MutablePropertyValues mpvs) {
            // 1. 检查是否存在不允许的字段存在,如果存在则删除
            checkAllowedFields(mpvs);
            // 2.检查是否存在Required 字段缺失的情况
            checkRequiredFields(mpvs);
            // 3. 进行注入
            applyPropertyValues(mpvs);
        }

    3件事:

    1. 检查是否存在不允许的字段存在,如果存在则删除
    2. 检查是否存在Required 字段缺失的情况,如果存在,则抛出异常
    3. 调用applyPropertyValues进行注入.代码如下:

          protected void applyPropertyValues(MutablePropertyValues mpvs) {
          try {
              // Bind request parameters onto target object.
              // 1. 进行注入
              getPropertyAccessor().setPropertyValues(mpvs, isIgnoreUnknownFields(), isIgnoreInvalidFields());
          }
          catch (PropertyBatchUpdateException ex) {
              // Use bind error processor to create FieldErrors.
              // 2. 如果抛出异常,则记录异常
              for (PropertyAccessException pae : ex.getPropertyAccessExceptions()) {
                  getBindingErrorProcessor().processPropertyAccessException(pae, getInternalBindingResult());
              }
          }
          }

      2件事:

      1. 进行注入.

        1. 调用getPropertyAccessor 获得ConfigurablePropertyAccessor.代码如下:

              protected ConfigurablePropertyAccessor getPropertyAccessor() {
                  return getInternalBindingResult().getPropertyAccessor();
              }
          1. 调用getInternalBindingResult,获得AbstractPropertyBindingResult.代码如下:

                protected AbstractPropertyBindingResult getInternalBindingResult() {
                // 1. 同样是lazy-init,当第一次调用时 ,调用initBeanPropertyAccess 进行初始化
                if (this.bindingResult == null) {
                initBeanPropertyAccess();
                }
                return this.bindingResult;
                }

            同样是lazy-init,当第一次调用时 ,调用initBeanPropertyAccess 进行初始化. initBeanPropertyAccess 代码如下:

                public void initBeanPropertyAccess() {
                Assert.state(this.bindingResult == null,
                "DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");
                this.bindingResult = createBeanPropertyBindingResult();
                }

            调用

                protected AbstractPropertyBindingResult createBeanPropertyBindingResult() {
                // 1. 实例化BeanPropertyBindingResult
                BeanPropertyBindingResult result = new BeanPropertyBindingResult(getTarget(),
                getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());
                if (this.conversionService != null) {
                // 2. 这个步骤是一定会执行的, 进行初始化
                result.initConversion(this.conversionService);
                }
                if (this.messageCodesResolver != null) {
                // 3. 设置messageCodesResolver
                result.setMessageCodesResolver(this.messageCodesResolver);
                }
                return result;
                }

            3件事:

            1. 实例化BeanPropertyBindingResult
            2. 这个步骤是一定会执行的, 进行初始化conversionService
            3. 设置messageCodesResolver
          2. 调用BeanPropertyBindingResult#getPropertyAccessor.

                public final ConfigurablePropertyAccessor getPropertyAccessor() {
                // 1. lazy-inits
                if (this.beanWrapper == null) {
                // 1.1 最终调用PropertyAccessorFactory#forBeanPropertyAccess,直接实例化了BeanWrapperImpl
                this.beanWrapper = createBeanWrapper();
                this.beanWrapper.setExtractOldValueForEditor(true);
                this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
                this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
                }
                return this.beanWrapper;
                }

            还是同样的味道,lazy-init,最终调用PropertyAccessorFactory#forBeanPropertyAccess,直接实例化了BeanWrapperImpl.

        2. 调用BeanPropertyBindingResult#setPropertyValues 进行注入.
      2. 如果在注入过程出现异常,则记录异常.

      其中 1.3 BeanPropertyBindingResult#setPropertyValues ,代码如下:

          public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
              throws BeansException {
      
          List propertyAccessExceptions = null;
          // 1. 获得propertyValues,一般情况下,此时传入的是MutablePropertyValues,因此直接通过MutablePropertyValues#getPropertyValueList 获取即可
          List propertyValues = (pvs instanceof MutablePropertyValues ?
                  ((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
          // 2. 遍历propertyValues,依次调用setPropertyValue 进行处理
          for (PropertyValue pv : propertyValues) {
      
                  // 删除一些无用的try-cath,减少篇幅...
                  setPropertyValue(pv);
      
          }
      
          if (propertyAccessExceptions != null) {
              PropertyAccessException[] paeArray =
                      propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
              throw new PropertyBatchUpdateException(paeArray);
          }
          }

      3件事:

      1. 获得propertyValues,一般情况下,此时传入的是MutablePropertyValues,因此直接通过MutablePropertyValues#getPropertyValueList 获取即可
      2. 遍历propertyValues,依次调用setPropertyValue 进行处理
      3. 如果propertyAccessExceptions != null,则意味在第2步处理中,出现了问题,则抛出PropertyBatchUpdateException.

      其中第2步,最终调用的是AbstractNestablePropertyAccessor#setPropertyValue,代码如下:

          public void setPropertyValue(PropertyValue pv) throws BeansException {
          PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
          if (tokens == null) {
              String propertyName = pv.getName();
              AbstractNestablePropertyAccessor nestedPa;
              try {
                  nestedPa = getPropertyAccessorForPropertyPath(propertyName);
              }
              catch (NotReadablePropertyException ex) {
                  throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
                          "Nested property in path '" + propertyName + "' does not exist", ex);
              }
              tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
              if (nestedPa == this) {
                  pv.getOriginalPropertyValue().resolvedTokens = tokens;
              }
              nestedPa.setPropertyValue(tokens, pv);
          }
          else {
              setPropertyValue(tokens, pv);
          }
          }
      1. 调用getPropertyAccessorForPropertyPath,处理对象导航,还是由于此处分析的最简单的场景,因此这里返回的就是当前类.
      2. 调用getPropertyNameTokens,这里处理的是集合的情况.同样,这里先不进行分析
      3. 调用AbstractNestablePropertyAccessor#setPropertyValue,进行赋值.代码如下:

            protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
            if (tokens.keys != null) {
                processKeyedProperty(tokens, pv);
            }
            else {
                processLocalProperty(tokens, pv);
            }
            }
        

        2件事:

        1. 如果 PropertyTokenHolder 中的keys 不等于null,则意味着是要对集合进行赋值,为什么?这个我们后面有解释.
        2. 否则调用 processLocalProperty进行处理.因为我们这里分析的是最简单的情况,因此会在这里进行处理.代码如下:
            private void processLocalProperty(PropertyTokenHolder tokens, PropertyValue pv) {
            // 1. 获得PropertyHandler
            PropertyHandler ph = getLocalPropertyHandler(tokens.actualName);
            // 2. 如果ph 等于null,或者 PropertyHandler 没有set方法
            if (ph == null || !ph.isWritable()) {
            // 2.1 如果该属性是可选的,则打印日志,否则抛出异常
            if (pv.isOptional()) {
        
                return;
            }
            else {
                throw createNotWritablePropertyException(tokens.canonicalName);
            }
            }
            // 3. 进行转换处理
            Object oldValue = null;
            Object originalValue = pv.getValue();
            Object valueToApply = originalValue;
            if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
                if (pv.isConverted()) {
                    valueToApply = pv.getConvertedValue();
                }
                else {
                    if (isExtractOldValueForEditor() && ph.isReadable()) {
        
                            oldValue = ph.getValue();
        
                    }
                    valueToApply = convertForProperty(
                            tokens.canonicalName, oldValue, originalValue, ph.toTypeDescriptor());
                }
                pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
            }
            // 4. 进行赋值
            ph.setValue(this.wrappedObject, valueToApply);
            }       
            }

        4件事

        1. 获得PropertyHandler,注意,这里调用的是BeanWrapperImpl.getLocalPropertyHandler代码如下:

                  protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) {
              PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
              if (pd != null) {
              return new BeanPropertyHandler(pd);
              }
              return null;
              }
          1. 调用getCachedIntrospectionResults 获得CachedIntrospectionResults.代码如下:

                    private CachedIntrospectionResults getCachedIntrospectionResults() {
                Assert.state(getWrappedInstance() != null, "BeanWrapper does not hold a bean instance");
                if (this.cachedIntrospectionResults == null) {
                // lazy-init,第一次调用时初始化
                this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass());
                }
                return this.cachedIntrospectionResults;
                }

            同样的调调,lazy-init,调用CachedIntrospectionResults#forClass获得CachedIntrospectionResults. 注意,这里传入的是target,也就是 People.class.代码如下:

            static CachedIntrospectionResults forClass(Class<?> beanClass) throws BeansException { // 1. 尝试从strongClassCache,softClassCache中获取,如果不为空,则直接返回. CachedIntrospectionResults results = strongClassCache.get(beanClass); if (results != null) { return results; } results = softClassCache.get(beanClass); if (results != null) { return results; } // 2. 初始化 results = new CachedIntrospectionResults(beanClass); ConcurrentMap<Class<?>, CachedIntrospectionResults> classCacheToUse; // 3. 如果当前给定的类是否是给定的ClassLoader 或者是其父ClassLoader 加载的 或者 判断给定的classLoader 是否是acceptedClassLoaders的子classLoader // 一般都是这个了,那么就使用strongClassCache,否则使用softClassCache if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) || isClassLoaderAccepted(beanClass.getClassLoader())) { classCacheToUse = strongClassCache; } else { if (logger.isDebugEnabled()) { logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe"); } classCacheToUse = softClassCache; } // 4. 加入缓存中 CachedIntrospectionResults existing = classCacheToUse.putIfAbsent(beanClass, results); return (existing != null ? existing : results); }

            4件事:

            1. 尝试从strongClassCache,softClassCache中获取,如果不为空,则直接返回.
            2. 否则,进行初始化.CachedIntrospectionResults.其中,有如下代码:

                      beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
                  Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
                  Introspector.getBeanInfo(beanClass));

              这里调用了java.beans.Introspector 获取BeanInfo,而CachedIntrospectionResults只是对BeanInfo包装而已,关于CachedIntrospectionResults的初始化,这里就不继续深入了,也没有必要.

            3. 如果当前给定的类是否是给定的ClassLoader 或者是其父ClassLoader 加载的 或者 判断给定的classLoader 是否是acceptedClassLoaders的子classLoader,那么就使用strongClassCache,否则使用softClassCache.一般就是strongClassCache
            4. 加入缓存中
          2. 调用CachedIntrospectionResults#getPropertyDescriptor 获得PropertyDescriptor.注意,这里返回的是java.beans.PropertyDescriptor.是关于java反射的.不懂的可以百度一下.
          3. 如果PropertyDescriptor 不等于null,就意味着在target 也就是 People 中找到了对应的属性.因此,直接返回BeanPropertyHandler.
          4. 否则,返回null.
        2. 如果ph 等于null,或者 PropertyHandler set方法不存在或者不是public的,如果该属性是可选的,则打印日志,否则抛出异常.
        3. 调用convertForProperty,进行属性的转换,这里就是真正属性转换的地方,同样,后面有解释
        4. 调用BeanPropertyHandler#setValue进行赋值.代码如下:

              public void setValue(final Object object, Object valueToApply) throws Exception {
              // 1. 获得该属性对应的set方法
              final Method writeMethod = (this.pd instanceof GenericTypeAwarePropertyDescriptor ?
                  ((GenericTypeAwarePropertyDescriptor) this.pd).getWriteMethodForActualAccess() :
                  this.pd.getWriteMethod());
              // 2. 如果该方法为私有的,则通过反射的方式,设置为可访问的
              if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {
              if (System.getSecurityManager() != null) {
                  AccessController.doPrivileged(new PrivilegedAction() {
                      @Override
                      public Object run() {
                          writeMethod.setAccessible(true);
                          return null;
                      }
                  });
              }
              else {
                  writeMethod.setAccessible(true);
              }
              }
              // 3. 进行赋值
              final Object value = valueToApply;
              if (System.getSecurityManager() != null) {
              try {
                  AccessController.doPrivileged(new PrivilegedExceptionAction() {
                      @Override
                      public Object run() throws Exception {
                          writeMethod.invoke(object, value);
                          return null;
                      }
                  }, acc);
              }
              catch (PrivilegedActionException ex) {
                  throw ex.getException();
              }
              }
              else {
              writeMethod.invoke(getWrappedInstance(), value);
              }
              }
              }
          

          3件事:

          1. 获得该属性对应的set方法
          2. 如果该方法为私有的,则通过反射的方式,设置为可访问的
          3. 通过writeMethod#invoke的方式调用set 方法 进行赋值.

          至此,关于简单属性的注入(String类型)就分析完了,接下来就占位符,集合,对象导航,属性转换来分别做处理.

          占位符处理

          在之前的分析过程中,我们跳过了占位符处理工程的分析,这里我们将配置文件改为如下:

              com.example.demo.name=${aaa:hi}

          还是分析对People name 属性的注入. 之前的步骤同之前的分析的一样.只不过在PropertyPlaceholderHelper# parseStringValue处开始了对占位符的处理.代码如下:

              protected String parseStringValue(
                          String value, PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {
          
                      StringBuilder result = new StringBuilder(value);
          
                      // 1. 通过String#indexOf 获取前缀(一般都是${)的下标
                      int startIndex = value.indexOf(this.placeholderPrefix);
                      // 2 如果存在
                      while (startIndex != -1) {
                          // 2.1 获得后缀,此时获得是最小的后缀,嵌套处理
                          int endIndex = findPlaceholderEndIndex(result, startIndex);
                          if (endIndex != -1) {// 3 如果endIndex 存在,
                              // 3.1 通过字符串的截取获得占位符,如${placeholder},那么此时获得的是placeholder
                              String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
                              String originalPlaceholder = placeholder;
                              // 3.2 进行循环引用的检查,如果存在,则抛出IllegalArgumentException
                              if (!visitedPlaceholders.add(originalPlaceholder)) {
                                  throw new IllegalArgumentException(
                                          "Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
                              }
                              // Recursive invocation, parsing placeholders contained in the placeholder key.
                              // 3.3 递归处理
                              placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
                              // Now obtain the value for the fully resolved key...
                              // 3.4 进行解析占位符
                              String propVal = placeholderResolver.resolvePlaceholder(placeholder);
                              // 3.5 如果propVal 不等于null并且 valueSeparator(默认为 :)不等于null,则此时意味有默认值,
                              // 那么此时调用placeholderResolver#resolvePlaceholder 进行解析,如果解析失败的话,则返回默认值
                              if (propVal == null && this.valueSeparator != null) {
                                  int separatorIndex = placeholder.indexOf(this.valueSeparator);
                                  if (separatorIndex != -1) {
                                      String actualPlaceholder = placeholder.substring(0, separatorIndex);
                                      String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                                      propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                                      if (propVal == null) {
                                          propVal = defaultValue;
                                      }
                                  }
                              }
          
                              // 3.6
                              if (propVal != null) {
                                  // Recursive invocation, parsing placeholders contained in the
                                  // previously resolved placeholder value.
                                  // 3.6.1 如果propVal 不等于null,则意味着解析成功,则继续递归处理,处理完后,进行替换,
          
                                  propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
                                  // 进行替换
                                  result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
                                  if (logger.isTraceEnabled()) {
                                      logger.trace("Resolved placeholder '" + placeholder + "'");
                                  }
                                  // 重新计算startIndex
                                  startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
                              }
                              else if (this.ignoreUnresolvablePlaceholders) {
                                  // Proceed with unprocessed value.
                                  // 3.6.2 如果没有解析成功并且ignoreUnresolvablePlaceholders,则重新计算startIndex
                                  startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
                              }
                              else {
                                  // 3.6.3 抛出IllegalArgumentException
                                  throw new IllegalArgumentException("Could not resolve placeholder '" +
                                          placeholder + "'" + " in value \"" + value + "\"");
                              }
                              // 3.7 从visitedPlaceholders 删除.该算法有点类似dfs.
                              visitedPlaceholders.remove(originalPlaceholder);
                          }
                          else {
                              // 2.2 将startIndex 设为-1,则意味着已经处理完了
                              startIndex = -1;
                          }
                      }
                      return result.toString();
                  }

          首先通过String#indexOf 获得 { 的下标,这里是存在的,因此继续处理。此时调用的是PropertyPlaceholderHelper#findPlaceholderEndIndex 获的后缀.比方说如果我们配置的是
          ${aa} 那么此时返回的就是}的下标,如果配置的是{aa}${bb}我们返回的就是,a后面的}的下标.代码如下:

              private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
                  int index = startIndex + this.placeholderPrefix.length();
                  int withinNestedPlaceholder = 0;
                  while (index < buf.length()) {
                      if (StringUtils.substringMatch(buf, index, this.placeholderSuffix)) {
                          if (withinNestedPlaceholder > 0) {
                              withinNestedPlaceholder--;
                              index = index + this.placeholderSuffix.length();
                          }
                          else {
                              return index;
                          }
                      }
                      else if (StringUtils.substringMatch(buf, index, this.simplePrefix)) {
                          withinNestedPlaceholder++;
                          index = index + this.simplePrefix.length();
                      }
                      else {
                          index++;
                      }
                  }
                  return -1;
              }
          

          接下来,进行字符截取,此时我们配置的是com.example.demo.name=${aaa:hi},截取后获得的是aaa:hi,

          第三步,将aaa:hi 作为参数,递归调用parseStringValue,由于此时aaa:hi 不存在${,因此直接返回的还是aaa:hi.

          接下来,判断是否存在:,对于当前,是存在的,因此对其进行截取分别获得actualPlaceholder,defaultValue.对于当前, actualPlaceholder = aaa, defaultValue = hi, 然后调用PlaceholderResolver#resolvePlaceholder获得值,如果actualPlaceholder 解析失败,则将propVal 设为默认值.关于这部分对于的源码如下:

              if (propVal == null && this.valueSeparator != null) {
                                  int separatorIndex = placeholder.indexOf(this.valueSeparator);
                                  if (separatorIndex != -1) {
                                      String actualPlaceholder = placeholder.substring(0, separatorIndex);
                                      String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
                                      propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
                                      if (propVal == null) {
                                          propVal = defaultValue;
                                      }
                                  }
                              }

          此刻,调用PlaceholderResolver#resolvePlaceholder,实际上调用的是在AbstractPropertyResolver#doResolvePlaceholders中实例化的PlaceholderResolver的实现.代码如下:

              private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
                      return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {
                          @Override
                          public String resolvePlaceholder(String placeholderName) {
                              return getPropertyAsRawString(placeholderName);
                          }
                      });
                  }

          因此这里最终调用PropertySourcesPropertyResolver#getPropertyAsRawString,代码如下:

                  protected String getPropertyAsRawString(String key) {
                      return getProperty(key, String.class, false);
                  }

          调用了
          org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(String, Class, boolean)方法,接下来的故事,就很之前一样了,这里就不在赘述了.

          集合处理

          接下来我们将application.properties 改为

              com.example.demo.address[0]=北京
              com.example.demo.address[1]=上海
              com.example.demo.address[2]=广州

          这里分析对People 中 address 属性的注入. 同样,前面的准备工作都一样,在对属性进行注入时,会调用AbstractNestablePropertyAccessor#setPropertyValue,代码如下:

              public void setPropertyValue(String propertyName, Object value) throws BeansException {
                      AbstractNestablePropertyAccessor nestedPa;
                      try {
                          // 1. 生成AbstractNestablePropertyAccessor
                          nestedPa = getPropertyAccessorForPropertyPath(propertyName);
                      }
                      catch (NotReadablePropertyException ex) {
                          throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
                                  "Nested property in path '" + propertyName + "' does not exist", ex);
                      }
                      // 2. 获得PropertyTokenHolder, getFinalPath 获得最终的PropertyName
                      PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedPa, propertyName));
                      // 3. 进行赋值
                      nestedPa.setPropertyValue(tokens, new PropertyValue(propertyName, value));
                  }

          这里调用了AbstractNestablePropertyAccessor#getPropertyNameTokens,代码如下:

              private PropertyTokenHolder getPropertyNameTokens(String propertyName) {
                      PropertyTokenHolder tokens = new PropertyTokenHolder();
                      String actualName = null;
                      List<String> keys = new ArrayList<String>(2);
                      int searchIndex = 0;
                      while (searchIndex != -1) {
                          // 1. 获得 [ 的下标
                          int keyStart = propertyName.indexOf(PROPERTY_KEY_PREFIX, searchIndex);
                          searchIndex = -1;
                          if (keyStart != -1) {
                              // 2 如果存在的话,则截取获得]的下标
                              int keyEnd = propertyName.indexOf(PROPERTY_KEY_SUFFIX, keyStart + PROPERTY_KEY_PREFIX.length());
                              if (keyEnd != -1) {
                                  // 3. 如果存在的话,则截取出actualName,例如[map],那么此时就是""
                                  if (actualName == null) {
                                      actualName = propertyName.substring(0, keyStart);
                                  }
                                  // 4. 截取出key 此时就是map
                                  String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd);
                                  if (key.length() > 1 && (key.startsWith("'") && key.endsWith("'")) ||
                                          (key.startsWith("\"") && key.endsWith("\""))) {
                                      key = key.substring(1, key.length() - 1);
                                  }
                                  keys.add(key);
                                  searchIndex = keyEnd + PROPERTY_KEY_SUFFIX.length();
                              }
                          }
                      }
                      tokens.actualName = (actualName != null ? actualName : propertyName);
                      tokens.canonicalName = tokens.actualName;
                      if (!keys.isEmpty()) {
                          // [ + StringUtils#collectionToDelimitedString(keys,][)+]
                          tokens.canonicalName += PROPERTY_KEY_PREFIX +
                                  StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) +
                                  PROPERTY_KEY_SUFFIX;
                          tokens.keys = StringUtils.toStringArray(keys);
                      }
                      return tokens;
                  }

          步骤如下:

          1. 首先获得[ 的下标

            1. 如果存在的话,则尝试获取]的下标.

              1. 如果存在的]下标的话, 则截取出属性值,对应于当前的情况,就是address.然后加入keys中.
            2. 如果不存在,则意味着不存在集合的情况.接下来的处理就很之前一样.

          注意在这里, keys 只加入了一个, 如:0,为什么呢?

          因为我们是循环处理的,这点很重要.后面的步骤都是在此基础上进行的,在处理完com.example.demo.address[0]=北京 后,再处理 com.example.demo.address[1]=上海 .为啥呢? 因为在AbstractPropertyAccessor#setPropertyValues中我们是通过遍历的方式处理的,代码如下:

                  for (PropertyValue pv : propertyValues) {
          
                              setPropertyValue(pv);
          
                  }       

          接下来, 在AbstractNestablePropertyAccessor#setPropertyValue,由于此刻keys 不等于null,因此会执行processKeyedProperty.代码如下:

              protected void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
                      if (tokens.keys != null) {
                          processKeyedProperty(tokens, pv);
                      }
                      else {
                          processLocalProperty(tokens, pv);
                      }
                  }

          processKeyedProperty 代码如下:

              private void processKeyedProperty(PropertyTokenHolder tokens, PropertyValue pv) {
                      // 1. 获得
                      Object propValue = getPropertyHoldingValue(tokens);
                      String lastKey = tokens.keys[tokens.keys.length - 1];
          
                      if (propValue.getClass().isArray()) {
                          // 省略....
                      }
          
                      else if (propValue instanceof List) {
                          PropertyHandler ph = getPropertyHandler(tokens.actualName);
                          Class<?> requiredType = ph.getCollectionType(tokens.keys.length);
                          List<Object> list = (List<Object>) propValue;
                          int index = Integer.parseInt(lastKey);
                          Object oldValue = null;
                          if (isExtractOldValueForEditor() && index < list.size()) {
                              oldValue = list.get(index);
                          }
                          Object convertedValue = convertIfNecessary(tokens.canonicalName, oldValue, pv.getValue(),
                                  requiredType, ph.nested(tokens.keys.length));
                          int size = list.size();
                          if (index >= size && index < this.autoGrowCollectionLimit) {
                              for (int i = size; i < index; i++) {
                                  try {
                                      list.add(null);
                                  }
                                  catch (NullPointerException ex) {
                                      throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
                                              "Cannot set element with index " + index + " in List of size " +
                                              size + ", accessed using property path '" + tokens.canonicalName +
                                              "': List does not support filling up gaps with null elements");
                                  }
                              }
                              list.add(convertedValue);
                          }
                          else {
                              try {
                                  list.set(index, convertedValue);
                              }
                              catch (IndexOutOfBoundsException ex) {
                                  throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
                                          "Invalid list index in property path '" + tokens.canonicalName + "'", ex);
                              }
                          }
                      }
          
                      else if (propValue instanceof Map) {
                          // 省略....
                      }
          
                      else {
                          throw new InvalidPropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
                                  "Property referenced in indexed property path '" + tokens.canonicalName +
                                  "' is neither an array nor a List nor a Map; returned value was [" + propValue + "]");
                      }
                  }

          2件事:

          1. 调用getPropertyHoldingValue 获得 属性对应的对象. 对于当前,就是获得People 中address 所对应的对象实例.代码如下:

                private Object getPropertyHoldingValue(PropertyTokenHolder tokens) {
                    // Apply indexes and map keys: fetch value for all keys but the last one.
                    // 1. 实例化PropertyTokenHolder
                    PropertyTokenHolder getterTokens = new PropertyTokenHolder();
                    getterTokens.canonicalName = tokens.canonicalName;
                    getterTokens.actualName = tokens.actualName;
                    getterTokens.keys = new String[tokens.keys.length - 1];
                    System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
            
                    Object propValue;
                    try {
                        // 2. 获得值
                        propValue = getPropertyValue(getterTokens);
                    }
                    catch (NotReadablePropertyException ex) {
                        throw new NotWritablePropertyException(getRootClass(), this.nestedPath + tokens.canonicalName,
                                "Cannot access indexed value in property referenced " +
                                "in indexed property path '" + tokens.canonicalName + "'", ex);
                    }
            
                    if (propValue == null) {
                        // null map value case
                        if (isAutoGrowNestedPaths()) {
                            int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
                            getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);
                            propValue = setDefaultValue(getterTokens);
                        }
                        else {
                            throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + tokens.canonicalName,
                                    "Cannot access indexed value in property referenced " +
                                    "in indexed property path '" + tokens.canonicalName + "': returned null");
                        }
                    }
                    return propValue;
                }
            1. 实例化PropertyTokenHolder
            2. 调用getPropertyValue,获得该属性所对应的对象–> propValue.代码如下:

                  protected Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
                  String propertyName = tokens.canonicalName;
                  String actualName = tokens.actualName;
                  // 1. 根据属性值获得PropertyHandler
                  PropertyHandler ph = getLocalPropertyHandler(actualName);
                  // 2. 如果PropertyHandler等于null或者没有get方法,抛出异常
                  if (ph == null || !ph.isReadable()) {
                      throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
                  }
                      // 3. 获得对应的属性值.
                      Object value = ph.getValue();
                      if (tokens.keys != null) {
                          // 4. 如果tokens.keys 不等于null,这里是不会执行的
                          if (value == null) {
                              // 4.1 如果autoGrowNestedPaths 值为true,则生成默认值,一般都会生成默认值的
                              if (isAutoGrowNestedPaths()) {
                                  value = setDefaultValue(tokens.actualName);
                              }
                              else {
                                  // 4.2 抛出异常
                                  throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
                                          "Cannot access indexed value of property referenced in indexed " +
                                                  "property path '" + propertyName + "': returned null");
                              }
                          }
                          String indexedPropertyName = tokens.actualName;
                          // apply indexes and map keys
                          // 5. 依次进行遍历
                          for (int i = 0; i < tokens.keys.length; i++) {
                              String key = tokens.keys[i];
                              // 5.1 如果value等于null,则抛出异常
                              if (value == null) {
                                  throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
                                          "Cannot access indexed value of property referenced in indexed " +
                                                  "property path '" + propertyName + "': returned null");
                              }
                              else if (value.getClass().isArray()) {
                                  // 省略...
                              }
                              else if (value instanceof List) {
                                  int index = Integer.parseInt(key);
                                  List list = (List) value;
                                  growCollectionIfNecessary(list, index, indexedPropertyName, ph, i + 1);
                                  value = list.get(index);
                              }
                              else if (value instanceof Set) {
                                  // 省略...
                              }
                              else if (value instanceof Map) {
                                  // 省略...
                              }
                              else {
                                  throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
                                          "Property referenced in indexed property path '" + propertyName +
                                                  "' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]");
                              }
                              indexedPropertyName += PROPERTY_KEY_PREFIX + key + PROPERTY_KEY_SUFFIX;
                          }
                      }
                      return value;
                  }
              
              1. 获得调用getLocalPropertyHandler获得PropertyHandler,这个我们前面已经分析过了.
              2. 如果PropertyHandler等于null或者没有get方法,抛出NotReadablePropertyException
              3. 获得对应的属性对象,也就是People 中的address.
              4. 如果tokens.keys 不等于null,对于当前来说,keys 不等于null,因此是会执行的.

                1. 如果autoGrowNestedPaths 值为true,则生成默认值,一般都会生成默认值的,否则抛出NullValueInNestedPathException.
                2. 依次进行遍历keys,针对value的不同类型做不同的处理,这里我们只看List,处理如下:

                  1. 将key 转为index,注意这里传入的是0.
                  2. 通过list.get(index)的方式获得下标所对应的对象.
            3. 如果propValue 等于null,如果autoGrowNestedPaths 属性值为true,则调用setDefaultValue 进行实例化,否则抛出NullValueInNestedPathException异常,一般情况下, autoGrowNestedPaths为true.同样,该方法一般情况下都会执行的.代码如下:

                  private PropertyValue createDefaultPropertyValue(PropertyTokenHolder tokens) {
                  TypeDescriptor desc = getPropertyTypeDescriptor(tokens.canonicalName);
                  Class type = desc.getType();
                  if (type == null) {
                      throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + tokens.canonicalName,
                              "Could not determine property type for auto-growing a default value");
                  }
                  Object defaultValue = newValue(type, desc, tokens.canonicalName);
                  return new PropertyValue(tokens.canonicalName, defaultValue);
                  }

              这样就实例化了,具体是怎么实例化的,这里就不展开了.

            4. 针对propValue的类型做不同的处理,如果该类型不是数字,List,Map,则抛出InvalidPropertyException.这里我们只分析list的情况,其他类似.

              1. 获得属性对应的对象
              2. 获得集合的泛型
              3. 获得下标
              4. 进行转换
              5. 如果下标大于集合的size,则将index – size 这段范围内,插入null值,最后在插入对应的值.否则,直接插入即可.
            5. 至此,集合的注入就分析完了.

              对象导航处理

              我们将配置文件改为如下:

                  com.example.demo.phone.number=1111111

              这里分析对People 中 phone 的number 属性的注入. 还是同样的套路,前面的准备工作都一样,最终在进行属性注入时,调用了AbstractNestablePropertyAccessor#setPropertyValue.在该方法中调用了getPropertyAccessorForPropertyPath 用于处理嵌套属性.代码如下:

                  protected AbstractNestablePropertyAccessor getPropertyAccessorForPropertyPath(String propertyPath) {
                          // 1. 通过PropertyAccessorUtils#getFirstNestedPropertySeparatorIndex 获得下标
                          int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
                          // Handle nested properties recursively.
                          if (pos > -1) {
                              // 如果存在的话,则意味着有嵌套存在,则递归处理,例如 map[my.key],
                              String nestedProperty = propertyPath.substring(0, pos);// nestedProperty = map[my
                              String nestedPath = propertyPath.substring(pos + 1); // nestedPath = key
                              // 3. 获得嵌套对象
                              AbstractNestablePropertyAccessor nestedPa = getNestedPropertyAccessor(nestedProperty);
                              // 4. 获取AbstractNestablePropertyAccessor,递归调用
                              return nestedPa.getPropertyAccessorForPropertyPath(nestedPath);
                          }
                          else {
                              // 如果不存在,则返回this
                              return this;
                          }
                      }

              2件事:

              1. 通过PropertyAccessorUtils#getFirstNestedPropertySeparatorIndex 获得下标.该方法最终调用了PropertyAccessorUtils#getNestedPropertySeparatorIndex,该方法处理的逻辑很简单,看是否存在.,代码如下:

                    private static int getNestedPropertySeparatorIndex(String propertyPath, boolean last) {
                        boolean inKey = false;
                        int length = propertyPath.length();
                        int i = (last ? length - 1 : 0);// 起始下标
                        while (last ? i >= 0 : i < length) {
                            switch (propertyPath.charAt(i)) {
                                case PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR: // [
                                case PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR:// ]
                                    inKey = !inKey;
                                    break;
                                case PropertyAccessor.NESTED_PROPERTY_SEPARATOR_CHAR: // .
                                    if (!inKey) {
                                        return i;
                                    }
                            }
                            if (last) {
                                i--;
                            }
                            else {
                                i++;
                            }
                        }
                        return -1;
                    }
                
              2. 如果存在嵌套属性,则递归处理

                1. 通过字符串截取,获得nestedProperty,nestedPath .对于当前来说, nestedProperty = phone, nestedPath = number
                2. 调用getNestedPropertyAccessor 获得AbstractNestablePropertyAccessor.代码如下:

                      private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) {
                      // 1. 如果nestedPropertyAccessors 等于null,则实例化
                      if (this.nestedPropertyAccessors == null) {
                          this.nestedPropertyAccessors = new HashMap();
                      }
                      // Get value of bean property.
                      // 2. 获取属性名
                      PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
                      String canonicalName = tokens.canonicalName;
                      // 3. 获得对应的值
                      Object value = getPropertyValue(tokens);
                      if (value == null || (value.getClass() == javaUtilOptionalClass && OptionalUnwrapper.isEmpty(value))) {
                          if (isAutoGrowNestedPaths()) {
                              value = setDefaultValue(tokens);
                          }
                          else {
                              throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
                          }
                      }
                  
                      // Lookup cached sub-PropertyAccessor, create new one if not found.
                      // 4. 获得访问嵌套对象
                      AbstractNestablePropertyAccessor nestedPa = this.nestedPropertyAccessors.get(canonicalName);
                      if (nestedPa == null || nestedPa.getWrappedInstance() !=
                              (value.getClass() == javaUtilOptionalClass ? OptionalUnwrapper.unwrap(value) : value)) {
                          if (logger.isTraceEnabled()) {
                              logger.trace("Creating new nested " + getClass().getSimpleName() + " for property '" + canonicalName + "'");
                          }
                          // 5. 如果不存在则创建一个,实例化的是BeanWrapperImpl
                          nestedPa = newNestedPropertyAccessor(value, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);
                          // Inherit all type-specific PropertyEditors.
                          copyDefaultEditorsTo(nestedPa);
                          copyCustomEditorsTo(nestedPa, canonicalName);
                          // 6. 存入缓存
                          this.nestedPropertyAccessors.put(canonicalName, nestedPa);
                      }
                      else {
                          if (logger.isTraceEnabled()) {
                              logger.trace("Using cached nested property accessor for property '" + canonicalName + "'");
                          }
                      }
                      return nestedPa;
                      }

                  6件事:

                  1. 如果nestedPropertyAccessors 等于null,则实例化. lazy-init
                  2. 调用getPropertyNameTokens 获得PropertyTokenHolder,对于当前,获得的是phone所对应的PropertyTokenHolder.这个方法,我们之前已经分析过了。
                  3. 调用getPropertyValue , 获得phone所对应的对象。关于这个方法,我们也已经分析过了,此时会将people中的phone 实例化.
                  4. 尝试从nestedPropertyAccessors缓存中获得AbstractNestablePropertyAccessor. 如果没有获得,则实例化一个BeanPropertyHandler.然后进行初始化后放入nestedPropertyAccessors.
                3. 递归调用getPropertyAccessorForPropertyPath.

                那我们的例子来说,第一次传入的参数是phone.number,有嵌套属性,因此会在实例化phone所对应后的AbstractNestablePropertyAccessor后,会递归调用getPropertyAccessorForPropertyPath,此时由于传入的参数是number,因此方法退出,因此该递归最终返回的是 phone所对应后的AbstractNestablePropertyAccessor.

                接着,AbstractNestablePropertyAccessor#getFinalPath,获得最终的路径,代码如下:

                     protected String getFinalPath(AbstractNestablePropertyAccessor pa, String nestedPath) {
                        if (pa == this) {
                            return nestedPath;
                        }
                        return nestedPath.substring(PropertyAccessorUtils.getLastNestedPropertySeparatorIndex(nestedPath) + 1);
                    }   

                由于pa 不等于this,因此会调用PropertyAccessorUtils#getLastNestedPropertySeparatorIndex 方法获得最后一个. 所对应的下标,通过字符串截取后,获得属性名,此时,会获得number。

                2个问题:

                1. 为什么pa 不等于 this?

                  还是拿例子来说话,this, 指的是people 所对应的AbstractNestablePropertyAccessor,pa 在当前来说,是phone所对应的AbstractNestablePropertyAccessor.明显不相等的.

                2. 为什么只需返回最后一个属性,就行了? 也就是

                  假如我们新增如下一个类型:

                      public class Operator {// 运营商
                  
                      private String name;
                  
                      // get set 忽略,自己加上即可..
                      }

                  然后将Phone 改为如下:

                      public class Phone {
                  
                      private String number;
                      private Operator operator;
                  
                      // get set 忽略,自己加上即可.. 
                      }

                  将配置文件加入如下配置:

                      com.example.demo.phone.operator.name=移动

                  为什么此时返回是name?

                  理由很简单,因为在调用AbstractNestablePropertyAccessor#getPropertyAccessorForPropertyPath时是递归处理的,该方法会首先实例化People 中的phone,接着实例化Phone 中operator所对应的Operator对象.后续的故事,就是直接赋值了,我们已经分析过了.

              属性转换处理

              这里,我们来看最后一个–>属性转换,将配置文件该为如下:

                  com.example.demo.age=11

              之前的准备工作,就不在赘述了,在最终进行赋值时,会调用
              AbstractNestablePropertyAccessor#processLocalProperty,而在该方法中的第三步,会调用AbstractNestablePropertyAccessor#convertForProperty进行转换处理,代码如下:

                  protected Object convertForProperty(String propertyName, Object oldValue, Object newValue, TypeDescriptor td)
                              throws TypeMismatchException {
              
                          return convertIfNecessary(propertyName, oldValue, newValue, td.getType(), td);
                      }
              

              最终调用TypeConverterDelegate#convertIfNecessary,代码如下:

              1. 获得自定义的PropertyEditor
              2. 从propertyEditorRegistry 获得自定义的ConversionService,这里使用的是org.springframework.boot.bind.RelaxedConversionService
              3. 如果PropertyEditor 等于null && conversionService 不等于null,&& newValue 不等于null,&& typeDescriptor 不等于null,则调用ConversionService#convert.

              这里由于不存在自定义的PropertyEditor,同时第2步获得的propertyEditorRegistry不等于null,因此最终会调用RelaxedConversionService#convert 进行转换,代码如下:

                  public Object convert(Object source, TypeDescriptor sourceType,
                              TypeDescriptor targetType) {
                          if (this.conversionService != null) {
                              try {
                                  return this.conversionService.convert(source, sourceType, targetType);
                              }
                              catch (ConversionFailedException ex) {
                                  // Ignore and try the additional converters
                              }
                          }
                          return this.additionalConverters.convert(source, sourceType, targetType);
                      }

              2件事:

              1. 如果conversionService 不等于null,则调用conversionService#convert 进行转换.对于当前,会执行这里, conversionService为GenericConversionService,代码如下:

                    public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
                        Assert.notNull(targetType, "Target type to convert to cannot be null");
                        // 1. 如果sourceType 等于null,则抛出ConversionFailedException
                        if (sourceType == null) {
                            Assert.isTrue(source == null, "Source must be [null] if source type == [null]");
                            return handleResult(null, targetType, convertNullSource(null, targetType));
                        }
                        // 2. 如果source不等于null,并且sourceType 不是source 的类型,则抛出IllegalArgumentException
                        if (source != null && !sourceType.getObjectType().isInstance(source)) {
                            throw new IllegalArgumentException("Source to convert from must be an instance of [" +
                                    sourceType + "]; instead it was a [" + source.getClass().getName() + "]");
                        }
                        // 3. 获得GenericConverter
                        GenericConverter converter = getConverter(sourceType, targetType);
                        if (converter != null) {
                            // 3.1 如果Converter,则通过ConversionUtils#invokeConverter 进行转换
                            Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
                            return handleResult(sourceType, targetType, result);
                        }
                        // 4. 当Converter 没有找到时 ,进行处理
                        return handleConverterNotFound(source, sourceType, targetType);
                    }

                4件事:

                1. 如果sourceType 等于null,则抛出ConversionFailedException
                2. 如果source不等于null,并且sourceType 不是source 的类型,则抛出IllegalArgumentException
                3. 获得GenericConverter,如果Converter 不等于null,则通过ConversionUtils#invokeConverter 进行转换.代码如下:

                      protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
                      // 1. 实例化ConverterCacheKey
                      ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
                      // 2. 尝试从converterCache 获取
                      GenericConverter converter = this.converterCache.get(key);
                      if (converter != null) {
                          return (converter != NO_MATCH ? converter : null);
                      }
                  
                      // 3. 从converters 获取
                      converter = this.converters.find(sourceType, targetType);
                      if (converter == null) {
                          // 4. 如果还没有得到,则返回默认的Converter
                          converter = getDefaultConverter(sourceType, targetType);
                      }
                  
                      if (converter != null) {
                          // 5. 如果不等于null,则放入缓存中
                          this.converterCache.put(key, converter);
                          return converter;
                      }
                  
                      // 6. 如果converter 等于null,则在converterCache中放入NO_MATCH
                      this.converterCache.put(key, NO_MATCH);
                      return null;
                      }
                  

                  6件事:

                  1. 实例化ConverterCacheKey
                  2. 尝试从converterCache 获取
                  3. 从converters 获取
                  4. 如果还没有得到,则返回默认的Converter
                  5. 如果不等于null,则放入缓存中
                  6. 如果converter 等于null,则在converterCache中放入NO_MATCH

                  对于当前,获得的是ConverterFactoryAdapter,其convert方法如下:

                          public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
                          if (source == null) {
                              return convertNullSource(sourceType, targetType);
                          }
                          return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
                      }

                  最终调用的是StringToNumber#convert 方法,代码如下:

                      public T convert(String source) {
                          if (source.isEmpty()) {
                              return null;
                          }
                          return NumberUtils.parseNumber(source, this.targetType);
                      }

                  至此,就将com.example.demo.age = 11 ,由原先的字符串,转换为了Integer.后面只需赋值即可了,关于这个,我们已经分析过了.

                4. 当Converter 没有找到时 ,进行处理
              2. 否则调用additionalConverters#convert 进行转换。

              来源:[]()

              赞(0) 打赏
              版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot 源码解析13-@ConfigurationProperties是如何生效的

              评论 抢沙发

              • 昵称 (必填)
              • 邮箱 (必填)
              • 网址

              © 2014 - 2020 Java 技术驿站   网站地图  | 

              icp 湘ICP备14000180

              >>> 网站已平稳运行:

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

              支付宝扫一扫打赏

              微信扫一扫打赏