@ConfigurationProperties 注解

前言

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

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

        private String name;

        private Integer age;

        private List<String> address;

        private Phone phone;

        // get set 忽略,自己加上即可..
    }


    public class Phone {

        private String number;

        // get set 忽略,自己加上即可..  

    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
在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


@ConfigurationProperties 注解支持两种方式.

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

    @Component
    @ConfigurationProperties(prefix = "com.example.demo")
    public class People {

        private String name;

        private Integer age;

        private List<String> address;

        private Phone phone;
    }   

    通过@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,看源码…

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

@RestController
public class TestController {

    @Autowired
    private People people;

    @RequestMapping("/get_name")
    public String getName() {

        return people.getName();
    }

    @RequestMapping("/get_address")
    public List<String> 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 是如何处理的吧.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

解析

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

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件事:
    获得类上的@ConfigurationProperties注解,如果注解存在,则调用postProcessBeforeInitialization 进行处理
    寻找工厂方法上是否有@ConfigurationProperties 注解,如果存在的话,则调用postProcessBeforeInitialization进行处理.对应的是@Bean的方式.

不管怎么样,最终都会调用ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization.代码如下:

private void postProcessBeforeInitialization(Object bean, String beanName,
        ConfigurationProperties annotation) {
    Object target = bean;
    // 1. 实例化PropertiesConfigurationFactory,该类实现了FactoryBean, MessageSourceAware, InitializingBean 接口,并进行一些属性的设置
    PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
            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件事:

    实例化PropertiesConfigurationFactory,该类实现了FactoryBean, MessageSourceAware, InitializingBean 接口,并进行一些属性的设置.
        将ConfigurationPropertiesBindingPostProcessor中的propertySources赋值给PropertiesConfigurationFactory

        通过调用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件事:

            调用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;
            }

                如果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,可是默认情况下,是不存在的.
                如果localValidator 等于null并且是jsr303环境的话,则实例化ValidatedLocalValidatorFactoryBean,并赋值给localValidator,这是一个lazy-init,ValidatedLocalValidatorFactoryBean 实现了ValidatorFactory, ApplicationContextAware, InitializingBean, DisposableBean,SmartValidator, javax.validation.Validator接口.
                如果不等于null,则直接返回
            如果validator不等于null并且该Validator 支持该bean的话,则supportsBean等于true,否则为false.

            如果当前类为Validator的子类
                如果supportsBean为true,则实例化ChainingValidator,则初始化ChainingValidator.进行返回
                否则强转为Validator,进行返回
            最后,如果supportsBean 则 返回Validator 否则 返回null

        如果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件事:

            如果defaultConversionService 等于null,则意味着是第一次调用,又是lazy-init 风格.
                实例化DefaultConversionService
                调用autowireBean进行注入依赖,此时会注入converters,genericConverters
                遍历converters,genericConverters 依次加入到conversionService的converters中
                赋值给defaultConversionService 
            如果不等于null,则直接返回 

    如果注解存在,这是肯定的,不然也不会执行该方法,则根据@ConfigurationProperties的值进行配置
        如果配置了prefix,或者value 值,则设置TargetName.这个后面解析的时候会用到该值.
    调用PropertiesConfigurationFactory#bindPropertiesToTarget,进行绑定

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件事:
    首先判断propertySources是否为null,如果为null的话,抛出异常.一般不会为null的.因为该类在实例化的时候,已经对其进行赋值了
    将hasBeenBound 设为true

    调用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<String> 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<String> 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件事:

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

getPropertySourcesPropertyValues.代码如下:

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

    根据names和relaxedTargetNames 生成PropertyNamePatternsMatcher.代码如下:

    private PropertyNamePatternsMatcher getPropertyNamePatternsMatcher(Set<String> names,
        Iterable<String> 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<String> relaxedNames = new HashSet<String>();
        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件事:
        如果ignoreUnknownFields 并且 target 不是map的子类,则返回DefaultPropertyNamePatternsMatcher,在@ConfigurationProperties中,ignoreUnknownFields默认是true.在此时,由于target 为People,因此返回DefaultPropertyNamePatternsMatcher.
        如果relaxedTargetNames 不等于null,则通过对relaxedTargetNames去重后,返回DefaultPropertyNamePatternsMatcher
        返回默认的

    返回PropertySourcesPropertyValues.其类图如下:

    PropertySourcesPropertyValues-type

    其构造器如下:

    PropertySourcesPropertyValues(PropertySources propertySources,
        Collection<String> 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件事:
        属性赋值.
        实例化PropertySourcesPropertyResolver

        遍历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);
        }
        }


            如果PropertySource是CompositePropertySource的子类,则调用processCompositePropertySource方法,而该方法最终还是调用了processPropertySource,做递归处理.

            如果PropertySource是EnumerablePropertySource的子类,则调用processEnumerablePropertySource.这里需要说明一下,我们是配置在application.properties中,那么其PropertySource 为 PropertiesPropertySource,是EnumerablePropertySource的子类,其继承结构如下:

            PropertiesPropertySource-type-tree
            因此,关于配置文件属性的注入,最终会在这里执行.
            否则,调用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);
            }
        }
        }
        }


        思路很简单,
            首先判断source中是否有属性的配置,如果有的话,则依次遍历之

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

                如果匹配的话,则调用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);
                }


                    如果resolvePlaceholders 为true,则调用PropertySourcesPropertyResolver#getProperty 处理,由于resolvePlaceholders 默认为true,因此一般都会执行这里.

                    否则,直接从EnumerablePropertySource 获取值即可.
                调用putIfAbsent 将值,属性名,保存到propertyValues 中. 

            其中2.1 会调用如下代码:

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


            最终调用如下代码:

            protected <T> T getProperty(String key, Class<T> 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件事

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

                    通过调用PropertySource#getProperty进行获取

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

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

                public String resolvePlaceholders(String text) {
            if (this.nonStrictHelper == null) {
            this.nonStrictHelper = createPlaceholderHelper(true);
            }
            return doResolvePlaceholders(text, this.nonStrictHelper);
            }


                如果nonStrictHelper等于null,则调用createPlaceholderHelper进行实例化.lazy-init 风格.

                调用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<String>());
                }


                最终调用如下代码:

                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();
                }


                3件事:
                    通过String#indexOf 获取前缀(一般都是${)的下标

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

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

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

                    com.example.demo.name=hi

视线回到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件事:
    检查是否存在不允许的字段存在,如果存在则删除
    检查是否存在Required 字段缺失的情况,如果存在,则抛出异常

    调用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件事:

        进行注入.

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

            protected ConfigurablePropertyAccessor getPropertyAccessor() {
                return getInternalBindingResult().getPropertyAccessor();
            }
                1
                2
                3

                调用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件事:
                    实例化BeanPropertyBindingResult
                    这个步骤是一定会执行的, 进行初始化conversionService
                    设置messageCodesResolver

                调用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. 
            调用BeanPropertyBindingResult#setPropertyValues 进行注入. 

        如果在注入过程出现异常,则记录异常.

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

    public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
        throws BeansException {

    List<PropertyAccessException> propertyAccessExceptions = null;
    // 1. 获得propertyValues,一般情况下,此时传入的是MutablePropertyValues,因此直接通过MutablePropertyValues#getPropertyValueList 获取即可
    List<PropertyValue> 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件事:
        获得propertyValues,一般情况下,此时传入的是MutablePropertyValues,因此直接通过MutablePropertyValues#getPropertyValueList 获取即可
        遍历propertyValues,依次调用setPropertyValue 进行处理
        如果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);
    }
    }


        调用getPropertyAccessorForPropertyPath,处理对象导航,还是由于此处分析的最简单的场景,因此这里返回的就是当前类.
        调用getPropertyNameTokens,这里处理的是集合的情况.同样,这里先不进行分析

        调用AbstractNestablePropertyAccessor#setPropertyValue,进行赋值.代码如下:

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


        2件事:
            如果 PropertyTokenHolder 中的keys 不等于null,则意味着是要对集合进行赋值,为什么?这个我们后面有解释.
            否则调用 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件事

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

                protected BeanPropertyHandler getLocalPropertyHandler(String propertyName) {
            PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
            if (pd != null) {
            return new BeanPropertyHandler(pd);
            }
            return null;
            }


                调用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件事:
                    尝试从strongClassCache,softClassCache中获取,如果不为空,则直接返回.

                    否则,进行初始化.CachedIntrospectionResults.其中,有如下代码:

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


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

            调用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<Object>() {
                    @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<Object>() {
                    @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件事:
                获得该属性对应的set方法
                如果该方法为私有的,则通过反射的方式,设置为可访问的
                通过writeMethod#invoke的方式调用set 方法 进行赋值.

    至此,关于简单属性的注入(String类型)就分析完了,接下来就占位符,集合,对象导航,属性转换来分别做处理. 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388
  • 389
  • 390
  • 391
  • 392
  • 393
  • 394
  • 395
  • 396
  • 397
  • 398
  • 399
  • 400
  • 401
  • 402
  • 403
  • 404
  • 405
  • 406
  • 407
  • 408
  • 409
  • 410
  • 411
  • 412
  • 413
  • 414
  • 415
  • 416
  • 417
  • 418
  • 419
  • 420
  • 421
  • 422
  • 423
  • 424
  • 425
  • 426
  • 427
  • 428
  • 429
  • 430
  • 431
  • 432
  • 433
  • 434
  • 435
  • 436
  • 437
  • 438
  • 439
  • 440
  • 441
  • 442
  • 443
  • 444
  • 445
  • 446
  • 447
  • 448
  • 449
  • 450
  • 451
  • 452
  • 453
  • 454
  • 455
  • 456
  • 457
  • 458
  • 459
  • 460
  • 461
  • 462
  • 463
  • 464
  • 465
  • 466
  • 467
  • 468
  • 469
  • 470
  • 471
  • 472
  • 473
  • 474
  • 475
  • 476
  • 477
  • 478
  • 479
  • 480
  • 481
  • 482
  • 483
  • 484
  • 485
  • 486
  • 487
  • 488
  • 489
  • 490
  • 491
  • 492
  • 493
  • 494
  • 495
  • 496
  • 497
  • 498
  • 499
  • 500
  • 501
  • 502
  • 503
  • 504
  • 505
  • 506
  • 507
  • 508
  • 509
  • 510
  • 511
  • 512
  • 513
  • 514
  • 515
  • 516
  • 517
  • 518
  • 519
  • 520
  • 521
  • 522
  • 523
  • 524
  • 525
  • 526
  • 527
  • 528
  • 529
  • 530
  • 531
  • 532
  • 533
  • 534
  • 535
  • 536
  • 537
  • 538
  • 539
  • 540
  • 541
  • 542
  • 543
  • 544
  • 545
  • 546
  • 547
  • 548
  • 549
  • 550
  • 551
  • 552
  • 553
  • 554
  • 555
  • 556
  • 557
  • 558
  • 559
  • 560
  • 561
  • 562
  • 563
  • 564
  • 565
  • 566
  • 567
  • 568
  • 569
  • 570
  • 571
  • 572
  • 573
  • 574
  • 575
  • 576
  • 577
  • 578
  • 579
  • 580
  • 581
  • 582
  • 583
  • 584
  • 585
  • 586
  • 587
  • 588
  • 589
  • 590
  • 591
  • 592
  • 593
  • 594
  • 595
  • 596
  • 597
  • 598
  • 599
  • 600
  • 601
  • 602
  • 603
  • 604
  • 605
  • 606
  • 607
  • 608
  • 609
  • 610
  • 611
  • 612
  • 613
  • 614
  • 615
  • 616
  • 617
  • 618
  • 619
  • 620
  • 621
  • 622
  • 623
  • 624
  • 625
  • 626
  • 627
  • 628
  • 629
  • 630
  • 631
  • 632
  • 633
  • 634
  • 635
  • 636
  • 637
  • 638
  • 639
  • 640
  • 641
  • 642
  • 643
  • 644
  • 645
  • 646
  • 647
  • 648
  • 649
  • 650
  • 651
  • 652
  • 653
  • 654
  • 655
  • 656
  • 657
  • 658
  • 659
  • 660
  • 661
  • 662
  • 663
  • 664
  • 665
  • 666
  • 667
  • 668
  • 669
  • 670
  • 671
  • 672
  • 673
  • 674
  • 675
  • 676
  • 677
  • 678
  • 679
  • 680
  • 681
  • 682
  • 683
  • 684
  • 685
  • 686
  • 687
  • 688
  • 689
  • 690
  • 691
  • 692
  • 693
  • 694
  • 695
  • 696
  • 697
  • 698
  • 699
  • 700
  • 701
  • 702
  • 703
  • 704
  • 705
  • 706
  • 707
  • 708
  • 709
  • 710
  • 711
  • 712
  • 713
  • 714
  • 715
  • 716
  • 717
  • 718
  • 719
  • 720
  • 721
  • 722
  • 723
  • 724
  • 725
  • 726
  • 727
  • 728
  • 729
  • 730
  • 731
  • 732
  • 733
  • 734
  • 735
  • 736
  • 737
  • 738
  • 739
  • 740
  • 741
  • 742
  • 743
  • 744
  • 745
  • 746
  • 747
  • 748
  • 749
  • 750
  • 751
  • 752
  • 753
  • 754
  • 755
  • 756
  • 757
  • 758
  • 759
  • 760
  • 761
  • 762
  • 763
  • 764
  • 765
  • 766
  • 767
  • 768
  • 769
  • 770
  • 771
  • 772
  • 773
  • 774
  • 775
  • 776
  • 777
  • 778
  • 779
  • 780
  • 781
  • 782
  • 783
  • 784
  • 785
  • 786
  • 787
  • 788
  • 789
  • 790
  • 791
  • 792
  • 793
  • 794
  • 795
  • 796
  • 797
  • 798
  • 799
  • 800
  • 801
  • 802
  • 803
  • 804
  • 805
  • 806
  • 807
  • 808
  • 809
  • 810
  • 811
  • 812
  • 813
  • 814
  • 815
  • 816
  • 817
  • 818
  • 819
  • 820
  • 821
  • 822
  • 823
  • 824
  • 825
  • 826
  • 827
  • 828
  • 829
  • 830
  • 831
  • 832
  • 833
  • 834
  • 835
  • 836
  • 837
  • 838
  • 839
  • 840
  • 841
  • 842
  • 843
  • 844
  • 845
  • 846
  • 847
  • 848
  • 849
  • 850
  • 851
  • 852
  • 853
  • 854
  • 855
  • 856
  • 857
  • 858
  • 859
  • 860
  • 861
  • 862
  • 863
  • 864
  • 865
  • 866
  • 867
  • 868
  • 869
  • 870
  • 871
  • 872
  • 873
  • 874
  • 875
  • 876
  • 877
  • 878
  • 879
  • 880
  • 881
  • 882
  • 883
  • 884
  • 885
  • 886
  • 887
  • 888
  • 889
  • 890
  • 891
  • 892
  • 893
  • 894
  • 895
  • 896
  • 897
  • 898
  • 899
  • 900
  • 901
  • 902
  • 903
  • 904
  • 905
  • 906
  • 907
  • 908
  • 909
  • 910
  • 911
  • 912
  • 913
  • 914
  • 915
  • 916
  • 917
  • 918
  • 919
  • 920
  • 921
  • 922
  • 923

占位符处理

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

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

1
  • 1

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

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();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

首先通过String#indexOf 获得 { 的下标,这里是存在的,因此继续处理。此时调用的是PropertyPlaceholderHelper#findPlaceholderEndIndex 获的后缀.比方说如果我们配置的是 
{aa} 那么此时返回的就是}的下标,如果配置的是{aa}{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);
}
  • 1
  • 2
  • 3

调用了 
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]=广州

1
2
3
  • 1
  • 2
  • 3

这里分析对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 keys = new ArrayList(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; 
}

步骤如下:

首先获得[ 的下标

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

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

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

for (PropertyValue pv : propertyValues) {

            setPropertyValue(pv);

}       
  • 1
  • 2
  • 3
  • 4
  • 5

接下来, 在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 + "]");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

2件事:

调用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;
}


    实例化PropertyTokenHolder

    调用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<Object> list = (List<Object>) 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;
    }


        获得调用getLocalPropertyHandler获得PropertyHandler,这个我们前面已经分析过了.
        如果PropertyHandler等于null或者没有get方法,抛出NotReadablePropertyException
        获得对应的属性对象,也就是People 中的address.

        如果tokens.keys 不等于null,对于当前来说,keys 不等于null,因此是会执行的.
            如果autoGrowNestedPaths 值为true,则生成默认值,一般都会生成默认值的,否则抛出NullValueInNestedPathException.
            依次进行遍历keys,针对value的不同类型做不同的处理,这里我们只看List,处理如下:
                将key 转为index,注意这里传入的是0.
                通过list.get(index)的方式获得下标所对应的对象. 

    如果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);
    }


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

针对propValue的类型做不同的处理,如果该类型不是数字,List,Map,则抛出InvalidPropertyException.这里我们只分析list的情况,其他类似.
    获得属性对应的对象
    获得集合的泛型
    获得下标
    进行转换
    如果下标大于集合的size,则将index - size 这段范围内,插入null值,最后在插入对应的值.否则,直接插入即可.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138

至此,集合的注入就分析完了. 
对象导航处理

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

com.example.demo.phone.number=1111111

1
  • 1

这里分析对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件事:

通过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;
}


如果存在嵌套属性,则递归处理
    通过字符串截取,获得nestedProperty,nestedPath .对于当前来说, nestedProperty = phone, nestedPath = number

    调用getNestedPropertyAccessor 获得AbstractNestablePropertyAccessor.代码如下:

    private AbstractNestablePropertyAccessor getNestedPropertyAccessor(String nestedProperty) {
    // 1. 如果nestedPropertyAccessors 等于null,则实例化
    if (this.nestedPropertyAccessors == null) {
        this.nestedPropertyAccessors = new HashMap<String, AbstractNestablePropertyAccessor>();
    }
    // 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件事:
        如果nestedPropertyAccessors 等于null,则实例化. lazy-init
        调用getPropertyNameTokens 获得PropertyTokenHolder,对于当前,获得的是phone所对应的PropertyTokenHolder.这个方法,我们之前已经分析过了。
        调用getPropertyValue , 获得phone所对应的对象。关于这个方法,我们也已经分析过了,此时会将people中的phone 实例化.
        尝试从nestedPropertyAccessors缓存中获得AbstractNestablePropertyAccessor. 如果没有获得,则实例化一个BeanPropertyHandler.然后进行初始化后放入nestedPropertyAccessors.
    递归调用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个问题:

    为什么pa 不等于 this?

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

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

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

    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=移动
        1

    为什么此时返回是name?

    理由很简单,因为在调用AbstractNestablePropertyAccessor#getPropertyAccessorForPropertyPath时是递归处理的,该方法会首先实例化People 中的phone,接着实例化Phone 中operator所对应的Operator对象.后续的故事,就是直接赋值了,我们已经分析过了.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135

属性转换处理

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

com.example.demo.age=11

1
  • 1

之前的准备工作,就不在赘述了,在最终进行赋值时,会调用 
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);
}
  • 1
  • 2

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

1

获得自定义的PropertyEditor
从propertyEditorRegistry 获得自定义的ConversionService,这里使用的是org.springframework.boot.bind.RelaxedConversionService

如果PropertyEditor 等于null && conversionService 不等于null,&& newValue 不等于null,&& typeDescriptor 不等于null,则调用ConversionService#convert.

…
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这里由于不存在自定义的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件事:

如果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件事:
    如果sourceType 等于null,则抛出ConversionFailedException
    如果source不等于null,并且sourceType 不是source 的类型,则抛出IllegalArgumentException

    获得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件事:
        实例化ConverterCacheKey
        尝试从converterCache 获取
        从converters 获取
        如果还没有得到,则返回默认的Converter
        如果不等于null,则放入缓存中
        如果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.后面只需赋值即可了,关于这个,我们已经分析过了.
    当Converter 没有找到时 ,进行处理

否则调用additionalConverters#convert 进行转换。

 

原文地址:https://blog.csdn.net/u011649691/article/details/79491674

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值