《Spring6核心技术》第11章:深度解析@Value注解

pivoteic
发布于 2023-9-21 11:54
浏览
0收藏

五、源码解析

5.2 为@Value修饰的属性赋值

本节主要对为@Value修饰的属性赋值的源码流程进行简单的分析,结合源码执行的时序图,会理解的更加深刻,本节的源码执行流程可以结合图11-3~11-4进行理解。具体分析步骤如下所示。

注意:为@Value修饰的属性赋值的源码流程的前半部分与本章5.1节分析源码的流程相同,这里,同样从AbstractAutowireCapableBeanFactory类的doCreateBean()方法开始分析。

(1)解析AbstractAutowireCapableBeanFactory类的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法

源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。重点关注如下代码片段。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {
 /************省略其他代码*************/
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
        if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
            throw bce;
        }
        else {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
        }
    }
 /************省略其他代码*************/
    return exposedObject;
}

可以看到,在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中,会调用populateBean方法为Bean的属性赋值。

(2)解析AbstractAutowireCapableBeanFactory类的populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)方法

源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw)。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    /**************省略其他代码*************/
    if (hasInstantiationAwareBeanPostProcessors()) {
        if (pvs == null) {
            pvs = mbd.getPropertyValues();
        }
        for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
            PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
                return;
            }
            pvs = pvsToUse;
        }
    }
    /**************省略其他代码*************/
}

可以看到,在populateBean()方法中,会调用InstantiationAwareBeanPostProcessor类的postProcessProperties()方法来处理属性或方法的值。实际上是调用的AutowiredAnnotationBeanPostProcessor类的postProcessProperties()方法。

(3)解析AutowiredAnnotationBeanPostProcessor类的postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties(PropertyValues pvs, Object bean, String beanName)。

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

可以看到,在AutowiredAnnotationBeanPostProcessor类的postProcessProperties()方法中,会调用findAutowiringMetadata()方法获取注解的元数据信息。

(4)解析AutowiredAnnotationBeanPostProcessor类的findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)方法

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs)。

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        synchronized (this.injectionMetadataCache) {
            metadata = this.injectionMetadataCache.get(cacheKey);
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                metadata = buildAutowiringMetadata(clazz);
                this.injectionMetadataCache.put(cacheKey, metadata);
            }
        }
    }
    return metadata;
}

由于在之前解析并获取@Value修饰的属性的代码流程中,已经完成了对@Value 修饰的属性的获取工作。所以,程序执行到findAutowiringMetadata()方法内部时,injectionMetadataCache缓存中已经有数据了。

(5)返回AutowiredAnnotationBeanPostProcessor类的postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法。在AutowiredAnnotationBeanPostProcessor类的postProcessProperties()方法中,调用了metadata对象的inject()方法为属性赋值。

(6)解析InjectionMetadata类的inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs)方法

源码详见:org.springframework.beans.factory.annotation.InjectionMetadata#inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs)。

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Collection<InjectedElement> checkedElements = this.checkedElements;
    Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements);
    if (!elementsToIterate.isEmpty()) {
        for (InjectedElement element : elementsToIterate) {
            element.inject(target, beanName, pvs);
        }
    }
}

可以看到,在InjectionMetadata类的inject()方法中,会循环遍历checkedElements集合,调用遍历出的每个InjectedElement对象的inject()方法为属性赋值。

注意:调用InjectedElement对象的inject()方法时,实际上可能会调用AutowiredFieldElement类的inject()方法、AutowiredMethodElement类的inject()方法或者InjectedElement类的inject()方法。这里,以调用AutowiredFieldElement类的inject()方法为例进行说明。

(7)解析AutowiredFieldElement类的inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)。

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    if (this.cached) {
        try {
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
        }
        catch (NoSuchBeanDefinitionException ex) {
            value = resolveFieldValue(field, bean, beanName);
        }
    }
    else {
        value = resolveFieldValue(field, bean, beanName);
    }
    if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

可以看到,在AutowiredFieldElement类的inject()方法中,会调用resolveFieldValue()方法来获取对应的属性值,如下所示。

value = resolveFieldValue(field, bean, beanName);

并通过反射向使用@Value注解标注的字段赋值,如下所示。

 field.set(bean, value);

(8)返回AbstractAutowireCapableBeanFactory类的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法。再来看下源码:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {
 /************省略其他代码*************/
    Object exposedObject = bean;
    try {
        populateBean(beanName, mbd, instanceWrapper);
        exposedObject = initializeBean(beanName, exposedObject, mbd);
    }
    catch (Throwable ex) {
        if (ex instanceof BeanCreationException bce && beanName.equals(bce.getBeanName())) {
            throw bce;
        }
        else {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName, ex.getMessage(), ex);
        }
    }
 /************省略其他代码*************/
    return exposedObject;
}

可以看到,在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中,为Bean的属性赋值后会调用initializeBean()方法对Bean进行初始化。

(9)解析AbstractAutowireCapableBeanFactory类的initializeBean(String beanName, Object bean, RootBeanDefinition mbd)方法

源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(String beanName, Object bean, RootBeanDefinition mbd)。

protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
    invokeAwareMethods(beanName, bean);
    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null), beanName, ex.getMessage(), ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

可以看到,在AbstractAutowireCapableBeanFactory类的initializeBean()方法中,会调用applyBeanPostProcessorsBeforeInitialization()方法在初始化之前执行一些逻辑,然后调用invokeInitMethods()执行真正的初始化操作,执行完Bean的初始化,会调用applyBeanPostProcessorsAfterInitialization()方法执行初始化之后的一些逻辑。

至此,为@Value修饰的属性赋值的源码流程分析完毕。

5.3 使用@Value获取属性的值

本节主要对使用@Value获取属性的值的源码流程进行简单的分析,结合源码执行的时序图,会理解的更加深刻,本节的源码执行流程可以结合图11-5~11-7进行理解。具体分析步骤如下所示。

注意:使用@Value获取属性的值的源码流程的前半部分与本章5.2节分析源码的流程相同,这里,从AutowiredFieldElement类的inject()方法开始分析。

(1)解析AutowiredFieldElement类的inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)方法

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs)。

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    if (this.cached) {
        try {
            value = resolvedCachedArgument(beanName, this.cachedFieldValue);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Unexpected removal of target bean for cached argument -> re-resolve
            value = resolveFieldValue(field, bean, beanName);
        }
    }
    else {
        value = resolveFieldValue(field, bean, beanName);
    }
    if (value != null) {
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

可以看到,在AutowiredFieldElement类的inject()方法中,会调用resolveFieldValue()方法处理获取属性的值。

(2)解析AutowiredFieldElement类的resolveFieldValue(Field field, Object bean, @Nullable String beanName)方法

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#resolveFieldValue(Field field, Object bean, @Nullable String beanName)。

@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
    /*************省略其他代码************/
    Object value;
    try {
        value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
    }
    catch (BeansException ex) {
        throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
    }
     /*************省略其他代码************/
    return value;
}

可以看到,在AutowiredFieldElement类的resolveFieldValue()方法中,会调用beanFactory对象的resolveDependency()方法,继续向下分析。

(3)解析DefaultListableBeanFactory类的resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter)

源码详见:org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter)。

@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    /***************省略其他代码**************/
    else {
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);
        if (result == null) {
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}

可以看到,在DefaultListableBeanFactory类的resolveDependency()方法中,会调用doResolveDependency()方法进一步处理。

(4)解析DefaultListableBeanFactory类的doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter)方法

源码详见:org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable SetautowiredBeanNames, @Nullable TypeConverter typeConverter)。

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
 /************省略其他代码*************/
    Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    if (value != null) {
        if (value instanceof String strValue) {
            String resolvedValue = resolveEmbeddedValue(strValue);
            BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                                 getMergedBeanDefinition(beanName) : null);
            value = evaluateBeanDefinitionString(resolvedValue, bd);
        }
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        try {
            return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
        }
        catch (UnsupportedOperationException ex) {
            // A custom TypeConverter which does not support TypeDescriptor resolution...
            return (descriptor.getField() != null ?
                    converter.convertIfNecessary(value, type, descriptor.getField()) :
                    converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
        }
    }
    /************省略其他代码*************/
}

可以看到,在DefaultListableBeanFactory类的doResolveDependency()方法中,如果当前获取到的数据是String类型,则调用resolveEmbeddedValue()方法进行处理。

(5)解析AbstractBeanFactory类的resolveEmbeddedValue(@Nullable String value)方法

源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#resolveEmbeddedValue(@Nullable String value)。

@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
    if (value == null) {
        return null;
    }
    String result = value;
    for (StringValueResolver resolver : this.embeddedValueResolvers) {
        result = resolver.resolveStringValue(result);
        if (result == null) {
            return null;
        }
    }
    return result;
}

可以看到,在AbstractBeanFactory类的resolveEmbeddedValue()中,会调用遍历出来的StringValueResolver对象的resolveStringValue()方法进行处理。此时,会进入AbstractEnvironment类的resolvePlaceholders(String text)方法。

(6)解析AbstractEnvironment类的resolvePlaceholders(String text)方法

源码详见:org.springframework.core.env.AbstractEnvironment#resolvePlaceholders(String text)。

@Override
public String resolvePlaceholders(String text) {
    return this.propertyResolver.resolvePlaceholders(text);
}

可以看到,在AbstractEnvironment类的resolvePlaceholders()方法中,会调用propertyResolver对象的resolvePlaceholders()方法进行处理。

(7)解析AbstractPropertyResolver类的resolvePlaceholders(String text)方法

源码详见:org.springframework.core.env.AbstractPropertyResolver#resolvePlaceholders(String text)。

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

可以看到,在AbstractPropertyResolver类的resolvePlaceholders()方法中,会调用doResolvePlaceholders()方法进一步处理。

(8)解析AbstractPropertyResolver类的doResolvePlaceholders(String text, PropertyPlaceholderHelper helper)方法

源码详见:org.springframework.core.env.AbstractPropertyResolver#doResolvePlaceholders(String text, PropertyPlaceholderHelper helper)。

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
    return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}

可以看到,在AbstractPropertyResolver类的doResolvePlaceholders()方法中,会解析 ${xxx.xxx} 这种占位,最终获取到 key = xxx.xxx,随后根据key去资源文件(xml、application.properties、Environment 等)中查找是否配置了这个key的值。其实是通过调用helper的replacePlaceholders()方法并以Lambda表达式的方式传入getPropertyAsRawString()方法实现的。

(9)解析PropertyPlaceholderHelper类的replacePlaceholders(String value, PlaceholderResolver placeholderResolver)方法

源码详见:org.springframework.util.PropertyPlaceholderHelper#replacePlaceholders(String value, PlaceholderResolver placeholderResolver)。

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

可以看到,在PropertyPlaceholderHelper类的replacePlaceholders()方法中,会调用parseStringValue()方法解析String类型的数据。

(10)解析PropertyPlaceholderHelper类的parseStringValue(String value, PlaceholderResolver placeholderResolver, @Nullable SetvisitedPlaceholders)方法

源码详见:org.springframework.util.PropertyPlaceholderHelper#parseStringValue(String value, PlaceholderResolver placeholderResolver, @Nullable SetvisitedPlaceholders)。

protected String parseStringValue(String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
    /***************省略其他代码****************/
    // Now obtain the value for the fully resolved key...
    String propVal = placeholderResolver.resolvePlaceholder(placeholder);
    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;
            }
        }
    }
    /**********省略其他代码***********/
}

在PropertyPlaceholderHelper类的parseStringValue()方法中重点关注如下代码片段。

String propVal = placeholderResolver.resolvePlaceholder(placeholder);

会调用placeholderResolver对象的resolvePlaceholder()方法传入解析 ${xxx.xxx} 占位符,获取到的key,其中key的形式为xxx.xxx。调用placeholderResolver对象的resolvePlaceholder()方法会最终调用PropertySourcesPropertyResolver类的getPropertyAsRawString()方法。

(11)解析PropertySourcesPropertyResolver类的getPropertyAsRawString(String key)方法

源码详见:org.springframework.core.env.PropertySourcesPropertyResolver#getPropertyAsRawString(String key)。

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

可以看到,在getPropertyAsRawString()方法中,会调用getProperty()方法获取属性的值。在调用getPropertyAsRawString()方法时,传入的Key的形式的规则就是:如果使用@Value标注的属性为 ${xxx.xxx} 占位符,则此处传入的Key的形式为xxx.xxx。

(12)解析PropertySourcesPropertyResolver类的getProperty(String key, ClasstargetValueType, boolean resolveNestedPlaceholders)方法

源码详见:org.springframework.core.env.PropertySourcesPropertyResolver#getProperty(String key, ClasstargetValueType, boolean resolveNestedPlaceholders)。

@Nullable
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 string) {
                    value = resolveNestedPlaceholders(string);
                }
                logKeyFound(key, propertySource, value);
                return convertValueIfNecessary(value, targetValueType);
            }
        }
    }
    if (logger.isTraceEnabled()) {
        logger.trace("Could not find key '" + key + "' in any property source");
    }
    return null;
}

在PropertySourcesPropertyResolver类的getProperty()方法中,会从 propertySources 资源中获取 key = xxx.xxx 的值,如果获取到一个对应的值,就会直接返回。其中,在propertySources中会封装PropertiesPropertySource、SystemEnvironmentPropertySource和ResourcePropertySource类型的对象,如图11-8所示。

《Spring6核心技术》第11章:深度解析@Value注解-鸿蒙开发者社区

其中,每种类型的对象中封装的信息如下所示。

  • PropertiesPropertySource:封装 JVM 环境变量中的键值对。
  • SystemEnvironmentPropertySource:封装操作系统环境变量中的键值对。
  • ResourcePropertySource:封装项目中application.properties、yml和xml等文件中的键值对。

通过调试PropertySourcesPropertyResolver类的getProperty()方法,可以发现,ResourcePropertySource对象中获取到对应的值,如图11-9所示。

《Spring6核心技术》第11章:深度解析@Value注解-鸿蒙开发者社区

从ResourcePropertySource对象中获取到对应的值就可以设置到被@Value注解标注的字段上。

至此,使用@Value获取属性的值的源码流程分析完毕。

六、总结

​@Value注解介绍完了,我们一起总结下吧!​

本章,详细介绍了@Value注解,首先介绍了@Value注解的源码和使用场景,并且对@Value注解的用法进行了简单的介绍。随后,介绍了@Value的使用案例。接下来,从解析并获取 @Value 修饰的属性、为 @Value 修饰属性赋值和使用@Value获取属性值三个方面分别详细介绍了@Value注解在Spring底层执行的源码时序图和源码流程。

七、思考

​既然学完了,就开始思考几个问题吧?​

关于@Value注解,通常会有如下几个经典面试题:

  • @Value注解的作用是什么?
  • @Value注解有哪些使用场景?
  • @Value向Bean的字段和方法注入值是如何实现的?
  • @Value注解在Spring内部的执行流程?
  • @Value注解在Spring源码中的执行流程与@Autowired注解有何区别?
  • 你在平时工作中,会在哪些场景下使用@Value注解?
  • 你从@Value注解的设计中得到了哪些启发?


文章转载自微服务:冰河技术

已于2023-9-21 11:55:00修改
收藏
回复
举报
回复
    相关推荐