51CTO首页
AI.x社区
博客
学堂
精品班
软考社区
免费课
企业培训
鸿蒙开发者社区
WOT技术大会
IT证书
公众号矩阵
移动端

深度解析@Value注解,你真的彻底了解过吗?

开发 架构
进一步学习并掌握@Value注解向Bean中注入值的案例和流程,从源码级别彻底掌握@Value注解在Spring底层的执行流程。

一、学习指引

Spring中的@Value注解,你真的彻底了解过吗?

在实际开发过程中,通常会有这样一种场景:将一些配置项写到配置文件中,在业务逻辑中会读取配置文件中的配置项,取出对应的值进行业务逻辑处理。Spring中提供的@Value注解就可以读取配置文件中的值。另外@Value注解也可以向Bean中的属性设置其他值。本章,就对@Value注解进行简单的介绍。

二、注解说明

关于@Value注解的一点点说明~~

@Value注解可以向Spring的Bean的属性中注入数据。并且支持Spring的EL表达式,可以通过${} 的方式获取配置文件中的数据。配置文件支持properties、XML、和YML文件。

1、注解源码

@Value注解的源码详见:org.springframework.beans.factory.annotation.Value。

/**
 * @author Juergen Hoeller
 * @since 3.0
 * @see AutowiredAnnotationBeanPostProcessor
 * @see Autowired
 * @see org.springframework.beans.factory.config.BeanExpressionResolver
 * @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
 String value();
}

从源码可以看出,@Value注解可以标注到字段、方法、参数和其他注解上,@Value注解中提供了一个String类型的value属性,具体含义如下所示。

  • value:指定要向Bean的属性中注入的数据,数据可以是配置文件中的配置项,并且支持EL表达式。

2、使用场景

在实际开发中,项目中难免会有一些配置信息,此时,就可以将这些配置信息统一写到配置文件中。随后使用@Value注解读取配置文件的值来向Spring中Bean的属性设置值。

例如,一些系统环境变量信息,数据库配置,系统通用配置等等,都可以保存到配置文件中,此时就可以使用Spring的EL表达式读取配置文件中的值。

3、用法

本节,主要介绍不通过配置文件注入属性和通过配置文件注入属性两种情况来介绍@Value注解的用法。

不通过配置文件注入属性

通过@Value可以将外部的值动态注入到Bean中,有如下几种用法。

(1)注入普通字符串。

@Value("normalString")
private String normalString;

(2)注入操作系统属性。

@Value("#{systemProperties['os.name']}")
private String osName;

(3)注入表达式的结果信息。

@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNum;

(4)注入其他Bean属性。

@Value("#{otherBean.name}")
private String name;

(5)注入文件资源。

@Value("classpath:config.properties")
private Resource resourceFile;

(6)注入URL资源。

@Value("http://www.baidu.com")
private Resource url;

通过配置文件注入属性

通过@Value(“${app.name}”)语法将属性文件的值注入到bean的属性中,

@Component
@PropertySource({"classpath:config.properties","classpath:config_${anotherfile.configinject}.properties"})
public class ConfigurationFileInject{
    @Value("${user.id}")
    private String userId; 

    @Value("${user.name}")
    private String userName; 

    @Value("${user.address}")
    private String userAddress; 
}

@Value中#{...}和${...}的区别

这里提供一个测试属性文件:test.properties,大致的内容如下所示。

server.name=server1,server2,server3
author.name=binghe

测试类Test:引入test.properties文件,作为属性的注入。

@Component
@PropertySource({"classpath:test.properties"})
public class Test {
}

${...}的用法

{}里面的内容必须符合SpEL表达式, 通过@Value(“${spelDefault.value}”)可以获取属性文件中对应的值,但是如果属性文件中没有这个属性,则会报错。可以通过赋予默认值解决这个问题,如下所示。

@Value("${author.name:binghe}")

上述代码的含义表示向Bean的属性中注入配置文件中的author.name属性的值,如果配置文件中没有author.name属性,则向Bean的属性中注入默认值binghe。例如下面的代码片段。

@Value("${author.name:binghe}")
private String name;

#{…}的用法

(1)SpEL:调用字符串Hello World的concat方法

@Value("#{'Hello World'.concat('!')}")
private String helloWorld;

(2)SpEL: 调用字符串的getBytes方法,然后调用length属性

@Value("#{'Hello World'.bytes.length}")
private int length;

${…}和#{…}混合使用

${...}和#{...}可以混合使用,如下文代码执行顺序:传入一个字符串,根据 "," 切分后插入列表中, #{}和${}配合使用,注意单引号。

@Value("#{'${server.name}'.split(',')}")
private List<String> servers;

注意:${}和#{}混合实用时,不能${}在外面,#{}在里面。因为Spring执行${}的时机要早于#{},当Spring执行外层的${}时,内部的#{}为空,会执行失败。

@Value注解用法总结

  • #{…} 用于执行SpEl表达式,并将内容赋值给属性。
  • ${…} 主要用于加载外部属性文件中的值。
  • #{…} 和${…} 可以混合使用,但是必须#{}外面,${}在里面。

三、使用案例

@Value的实现案例,我们一起实现吧~~

本节,就基于@Value注解实现向Bean属性中赋值的案例,具体的实现步骤如下所示。

1、新增test.properties配置文件

在spring-annotation-chapter-11工程下的resources目录下新增test.properties配置文件,内容如下所示。

db.url=jdbc:mysql://localhost:3306/test

2、新增ValueName类

ValueName类的源码详见:spring-annotation-chapter-11工程下的io.binghe.spring.annotation.chapter11.bean.ValueName。

@Component
public class ValueName {
    private String name;
    public ValueName() {
        this.name = "binghe";
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

可以看到,ValueName类上标注了@Component注解,说明当Spring的IOC容器启动时,会向IOC容器中注入ValueName类的Bean对象。

3、新增ValueConfig类

ValueConfig类的源码详见:spring-annotation-chapter-11工程下的io.binghe.spring.annotation.chapter11.config.ValueConfig。

@Configuration
@ComponentScan(value = {"io.binghe.spring.annotation.chapter11"})
@PropertySource(value = {"classpath:test.properties"})
public class ValueConfig {
    /**
     * 注入普通字符串
     */
    @Value("normalString")
    private String normalString;
    /**
     * 注入操作系统名称
     */
    @Value("#{systemProperties['os.name']}")
    private String osName;
    /**
     * 注入表达式的结果
     */
    @Value("#{ T(java.lang.Math).random() * 100.0 }")
    private double randomNum;
    /**
     * 注入其他Bean的属性
     */
    @Value("#{valueName.name}")
    private String name;
    /**
     * 注入配置文件中的值
     */
    @Value("${db.url}")
    private String dbUrl;
    @Override
    public String toString() {
        return "ValueConfig{" +
                "normalString='" + normalString + '\'' +
                ", osName='" + osName + '\'' +
                ", randomNum=" + randomNum +
                ", name='" + name + '\'' +
                ", dbUrl='" + dbUrl + '\'' +
                '}';
    }
}

可以看到,在ValueConfig类上标注了@Configuration注解,说明ValueConfig类是Spring的配置类。使用@ComponentScan注解指定了扫描的包名是io.binghe.spring.annotation.chapter11。并且使用@PropertySource注解导入了test.properties配置文件。ValueConfig类的字段通过@Value注解注入对应的属性值,代码中有详细的注释,这里不再赘述。

4、新增ValueTest类

ValueTest类的源码详见:spring-annotation-chapter-11工程下的io.binghe.spring.annotation.chapter11.ValueTest。

public class ValueTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ValueConfig.class);
        ValueConfig valueConfig = context.getBean(ValueConfig.class);
        System.out.println(valueConfig.toString());
    }
}

可以看到,ValueTest类是案例程序的测试类,实现的代码比较简单,这里不再赘述。

5、运行ValueTest类

运行ValueTest类的main()方法,输出的结果信息如下所示。

ValueConfig{normalString='normalString', osName='Windows 10', randomNum=60.704013358598715, name='binghe', dbUrl='jdbc:mysql://localhost:3306/test'}

可以看到,在ValueTest类中的各个字段值都输出了正确的结果数据。

说明:使用@Value注解向Bean的属性中正确设置了值

四、源码时序图

结合时序图理解源码会事半功倍,你觉得呢?

本节,就以源码时序图的方式,直观的感受下@Value注解在Spring源码层面的执行流程。本节,会从解析并获取 @Value 修饰的属性、为 @Value 修饰属性赋值和使用@Value获取属性值三个方面分析源码时序图。

注意:本节以单例Bean为例分析源码时序图,并且基于@Value注解标注到类的字段上的源码时序图为例进行分析,@Value注解标注到类的方法上的源码时序图与标注到字段上的源码时序图基本相同,不再赘述。

1、解析并获取@Value修饰的属性

本节,就简单介绍下解析并获取@Value修饰的属性的源码时序图,整体如图11-1~11-2所示。



图片

由图11-1~11-2可以看出,解析并获取@Value修饰的属性的流程中涉及到ValueTest类、AnnotationConfigApplicationContext类、AbstractApplicationContext类、DefaultListableBeanFactory类、AbstractBeanFactory类、AbstractAutowireCapableBeanFactory类和AutowiredAnnotationBeanPostProcessor类。具体的源码执行细节参见源码解析部分。

2、为@Value修饰的属性赋值

本节,就简单介绍下为@Value修饰的属性赋值的源码时序图,整体如图11-3~11-4所示。

图片



由图11-3~11-4所示,为@Value修饰的属性赋值流程涉及到ValueTest类、AnnotationConfigApplicationContext类、AbstractApplicationContext类、DefaultListableBeanFactory类、AbstractBeanFactory类、AbstractAutowireCapableBeanFactory类、AutowiredAnnotationBeanPostProcessor类、InjectionMetadata类和AutowiredFieldElement类。具体的源码执行细节参见源码解析部分。

3、使用@Value获取属性的值

本节,就简单介绍下使用@Value注解获取属性的值的源码时序图,整体如图11-5~11-7所示。

图片


图片


图片

由图11-5~11-7所示,使用@Value获取属性的值的流程涉及到ValueTest类、AnnotationConfigApplicationContext类、AbstractApplicationContext类、DefaultListableBeanFactory类、AbstractBeanFactory类、AbstractAutowireCapableBeanFactory类、AutowiredAnnotationBeanPostProcessor类、InjectionMetadata类、AutowiredFieldElement类、AbstractEnvironment类、AbstractPropertyResolver类、PropertyPlaceholderHelper类和PropertySourcesPropertyResolver类。具体的源码执行细节参见源码解析部分。

五、源码解析

源码时序图整清楚了,那就整源码解析呗!

本节,主要分析@Value注解在Spring源码层面的执行流程,同样的,本节也会从解析并获取 @Value 修饰的属性、为 @Value 修饰属性赋值和使用@Value获取属性值三个方面分析源码执行流程,并且结合源码执行的时序图,会理解的更加深刻。

注意:本节以单例Bean为例分析,并且基于@Value注解标注到类的字段上的源码流程为例进行分析,@Value注解标注到类的方法上的源码流程与标注到字段上的源码流程基本相同,不再赘述。

1、解析并获取@Value修饰的属性

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

注意:解析并获取 @Value 修饰属性源码流程的前半部分与第7章5.3节分析源码的流程相同,这里,从AbstractBeanFactory类的doGetBean()方法开始分析。

(1)解析AbstractBeanFactory类的doGetBean(String name, ClassrequiredType, Object[] args, boolean typeCheckOnly)方法

源码详见:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean(String name, ClassrequiredType, Object[] args, boolean typeCheckOnly)。重点关注如下代码片段。

protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
 /***********省略其他代码***********/
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
                destroySingleton(beanName);
                throw ex;
            }
        });
        beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }
 /***********省略其他代码***********/
    return adaptBeanInstance(name, beanInstance, requiredType);
}

可以看到,在AbstractBeanFactory类的doGetBean()方法中,如果是单例Bean,会调用getSingleton()方法创建单例Bean,实际执行的是Lambda表达式中的createBean()方法来创建单例Bean。

(2)解析AbstractAutowireCapableBeanFactory类的createBean(String beanName, RootBeanDefinition mbd, Object[] args)。

源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。

@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException { 
 /**************省略其他代码***************/
    try {
        Object beanInstance = doCreateBean(beanName, mbdToUse, args);
        if (logger.isTraceEnabled()) {
            logger.trace("Finished creating instance of bean '" + beanName + "'");
        }
        return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
    }
}

可以看到,在AbstractAutowireCapableBeanFactory类的createBean()方法中,会调用doCreateBean()方法创建Bean对象。

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

源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)。此时重点关注创建Bean实例的代码片段,如下所示。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {
    BeanWrapper instanceWrapper = null;
    if (mbd.isSingleton()) {
        instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
    }
    if (instanceWrapper == null) {
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    /***********省略其他代码**********/
    return exposedObject;
}

(4)解析AbstractAutowireCapableBeanFactory类的(String beanName, RootBeanDefinition mbd, Object[] args)方法

源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args)。

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
    // Make sure bean class is actually resolved at this point.
    Class<?> beanClass = resolveBeanClass(mbd, beanName);
    if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
        throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
    }

    Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
    if (instanceSupplier != null) {
        return obtainFromSupplier(instanceSupplier, beanName);
    }

    if (mbd.getFactoryMethodName() != null) {
        return instantiateUsingFactoryMethod(beanName, mbd, args);
    }
    boolean resolved = false;
    boolean autowireNecessary = false;
    if (args == null) {
        synchronized (mbd.constructorArgumentLock) {
            if (mbd.resolvedConstructorOrFactoryMethod != null) {
                resolved = true;
                autowireNecessary = mbd.constructorArgumentsResolved;
            }
        }
    }
    if (resolved) {
        if (autowireNecessary) {
            return autowireConstructor(beanName, mbd, null, null);
        }
        else {
            return instantiateBean(beanName, mbd);
        }
    }
    Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
    if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
        mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
        return autowireConstructor(beanName, mbd, ctors, args);
    }
    ctors = mbd.getPreferredConstructors();
    if (ctors != null) {
        return autowireConstructor(beanName, mbd, ctors, null);
    }
    return instantiateBean(beanName, mbd);
}

可以看到,createBeanInstance()方法会创建Bean的实例并返回BeanWrapper对象。

(5)返回AbstractAutowireCapableBeanFactory类的doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)方法,此时,重点关注如下代码片段。

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
 /*************省略其他代码***************/
    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex);
            }
            mbd.markAsPostProcessed();
        }
    }
 /*************省略其他代码***************/
}

可以看到,在AbstractAutowireCapableBeanFactory类的doCreateBean()方法中会调用applyMergedBeanDefinitionPostProcessors()方法的主要作用就是:获取@Value、@Autowired、@PostConstruct、@PreDestroy等注解标注的字段和方法,然后封装到InjectionMetadata对象中,最后将所有的InjectionMetadata对象存入injectionMeatadataCache缓存中。

(6)解析AbstractAutowireCapableBeanFactory类的applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)方法

源码详见:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName)。

protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
    for (MergedBeanDefinitionPostProcessor processor : getBeanPostProcessorCache().mergedDefinition) {
        processor.postProcessMergedBeanDefinition(mbd, beanType, beanName);
    }
}

可以看到,在AbstractAutowireCapableBeanFactory类的applyMergedBeanDefinitionPostProcessors()方法中,会调用processor的postProcessMergedBeanDefinition()方法处理BeanDefinition信息。

(7)解析AutowiredAnnotationBeanPostProcessor类postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)。

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName)

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    findInjectionMetadata(beanName, beanType, beanDefinition);
}

可以看到,在AutowiredAnnotationBeanPostProcessor类postProcessMergedBeanDefinition()方法中会调用findInjectionMetadata()方法来获取标注了注解的字段或者方法。

(8)解析AutowiredAnnotationBeanPostProcessor类的findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition)

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition)。

private InjectionMetadata findInjectionMetadata(String beanName, Class<?> beanType, RootBeanDefinition beanDefinition) {
    InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
    metadata.checkConfigMembers(beanDefinition);
    return metadata;
}

可以看到,在AutowiredAnnotationBeanPostProcessor类的findInjectionMetadata()方法中,调用了findAutowiringMetadata方法来解析并获取@Value、@Autowired、@Inject等注解修饰的属性或者方法。

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

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiringMetadata(String beanName, Class<?> clazz, 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;
}

AutowiredAnnotationBeanPostProcessor类的findAutowiringMetadata()方法需要重点关注下,findAutowiringMetadata()方法最核心的功能就是对传递进来的每个类进行筛选判断是否被@Value、@Autowired、@Inject注解修饰的方法或者属性,如果是被 @Value、@Autowired、@Inject注解修饰的方法或者属性,就会将这个类记录下来,存入injectionMetadataCache缓存中,为后续的 DI 依赖注作准备。

首次调用findAutowiringMetadata()方法时,会调用buildAutowiringMetadata()方法来查找使用@Value、@Autowired、@Inject注解修饰的方法或者属性。

(10)解析AutowiredAnnotationBeanPostProcessor类的buildAutowiringMetadata(Class<?> clazz)方法。

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata(Class<?> clazz)。方法中会查找@Value、@Autowired、@Inject注解修饰的方法或者属性,这里以查找属性为例,重点关注如下代码片段。

private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
    if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
        return InjectionMetadata.EMPTY;
    }

    List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
    Class<?> targetClass = clazz;

    do {
        final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

        ReflectionUtils.doWithLocalFields(targetClass, field -> {
            MergedAnnotation<?> ann = findAutowiredAnnotation(field);
            if (ann != null) {
                if (Modifier.isStatic(field.getModifiers())) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Autowired annotation is not supported on static fields: " + field);
                    }
                    return;
                }
                boolean required = determineRequiredStatus(ann);
                currElements.add(new AutowiredFieldElement(field, required));
            }
        });
  /**************省略其他代码****************/
        elements.addAll(0, currElements);
        targetClass = targetClass.getSuperclass();
    }
    while (targetClass != null && targetClass != Object.class);
    return InjectionMetadata.forElements(elements, clazz);
}

可以看到,在AutowiredAnnotationBeanPostProcessor类的buildAutowiringMetadata()方法中,获取到类上所有的字段,然后遍历每个字段,判断是否标注了 @Value、@Autowired和@Inject注解,如果标注了 @Value、@Autowired和@Inject注解,直接封装成 AutowiredFieldElement 对象,然后保存到一个名为 currElements集合中。

指的一提的是,如果解析到的字段是静态字段,则直接返回,这就是为什么Spring不会对类中的静态字段赋值的原因。如下代码片段所示。

if (Modifier.isStatic(field.getModifiers())) {
    if (logger.isInfoEnabled()) {
        logger.info("Autowired annotation is not supported on static fields: " + field);
    }
    return;
}

在AutowiredAnnotationBeanPostProcessor类的buildAutowiringMetadata()方法的最后,则将标注了@Value、@Autowired和@Inject注解的字段封装到 InjectionMetadata 对象中,如下所示。

return InjectionMetadata.forElements(elements, clazz);

最终回到AutowiredAnnotationBeanPostProcessor类的findAutowiringMetadata()方法中,将InjectionMetadata 对象存入injectionMetadataCache缓存中。如下所示。

metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);

另外,在AutowiredAnnotationBeanPostProcessor类的buildAutowiringMetadata()方法中,调用了findAutowiredAnnotation()方法来获取注解信息,如下所示。

MergedAnnotation<?> ann = findAutowiredAnnotation(field);

接下来,看看这个findAutowiredAnnotation()方法。

(11)解析AutowiredAnnotationBeanPostProcessor类的findAutowiredAnnotation(AccessibleObject ao)方法

源码详见:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#findAutowiredAnnotation(AccessibleObject ao)。

private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {
    MergedAnnotations annotations = MergedAnnotations.from(ao);
    for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
        MergedAnnotation<?> annotation = annotations.get(type);
        if (annotation.isPresent()) {
            return annotation;
        }
    }
    return null;
}

可以看到,在AutowiredAnnotationBeanPostProcessor类的findAutowiredAnnotation()方法中,会遍历autowiredAnnotationTypes集合,通过遍历出的每个autowiredAnnotationTypes集合中的元素从annotations中获取MergedAnnotation对象annotation,如果annotation存在,则返回annotation。否则返回null。

这里,需要关注下autowiredAnnotationTypes集合,在AutowiredAnnotationBeanPostProcessor类的构造方法中向autowiredAnnotationTypes集合中添加元素,如下所示。

public AutowiredAnnotationBeanPostProcessor() {
    this.autowiredAnnotationTypes.add(Autowired.class);
    this.autowiredAnnotationTypes.add(Value.class);

    try {
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>) ClassUtils.forName("jakarta.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("'jakarta.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // jakarta.inject API not available - simply skip.
    }

    try {
        this.autowiredAnnotationTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
        logger.trace("'javax.inject.Inject' annotation found and supported for autowiring");
    }
    catch (ClassNotFoundException ex) {
        // javax.inject API not available - simply skip.
    }
}

可以看到,在AutowiredAnnotationBeanPostProcessor类的构造方法中,向autowiredAnnotationTypes集合中添加了@Autowired注解、@Value注解和@Inject注解。所以,@Autowired注解赋值的流程和@Value注解赋值的流程基本一致。

至此,解析并获取@Value注解修饰的属性的源码流程分析完毕。

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修饰的属性赋值的源码流程分析完毕。

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所示。

图片

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

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

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

图片

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

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

六、总结

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

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

责任编辑:姜华 来源: 冰河技术
相关推荐
深度解析物联网 真的了解
物联网容括了智能感知、识别技术、普适计算等通信感知技术,以及人工智能、大数据、云存储等新兴科技,真正实现现实、虚拟的有效融合,因此被称为继计算机、互联网之后世界信息产业发展的第三次浪潮。

2016-01-13 10:34:57

物联网 物联网技术
@Configuration注解天天用,真的了解
通过CGLIB创建代理,这些不是我们关心的,我们主要关心的是它是如何拦截配置方法的。所以这里我们主要关注的上面createClass方法中设置的CALLBACKS。在这个数组中通过名称也就清楚核心拦截器是BeanMethodInterceptor。

2024-01-08 08:27:11

注解 Bean 代理
真的了解 Htap
既然HTAP负载并不是业务系统一定要追求的,那么为什么现在我们随便看到一个分布式数据库,就一定说自己是HTAP数据库呢?这实际上是和分布式数据库的发展历史分不开的。分布式数据库刚刚出现的时候,主要还是为了高并发的OLTP写入业务。

2022-07-26 00:00:22

HTAP 系统 数据库
真的了解 DevOps
DevOps(开发运维)如今是科技人士挂在嘴边的热词,但遗憾的是,类似圣经,每个人都引用DevOps的只言片语,但真正理解并能执行的人极少。

2014-04-17 16:42:03

DevOps
天天都在使用的 Java 注解真的了解
注解(Annotation)是一种可以放在Java类上,方法上,属性上,参数前面的一种特殊的注释,用来注释注解的注解叫做元注解。元注解我们平常不会编写,只需要添加到我们自己编写的注解上即可,。

2020-11-20 07:58:04

Java
真的了解物联网
物联网指的是由相互连接的计算设备或“事物”组成的网络。物联网设备通常配备了传感器、软件和其他技术以实现数据收集和交换,通常通过互联网进行。任何符合处理和交换数据,以及连接到互联网的简单特征的设备都被认为是物联网设备。

2023-03-16 10:49:55

GoLang:真的了解 HTTPS
由于笔者对HTTPS的认知也仅停留在“HTTPS比HTTP更安全”的层面上,在项目开发中遇到很多通信相关的问题经常束手无策,因此沉下心来,认认真真学习了HTTPS并记录成此文。

2020-02-27 10:49:26

HTTPS 网络协议 TCP
真的了解传感网
很多人在看到传感网时脑子里不觉的就会联想到物联网,对于传感网和物联网之间的关系也存在不同的声音。想必两者之间肯定有很多相似的地方,接下来就围绕传感网做一个详细的分享。

2019-09-16 08:40:42

真的了解Hybrid APP
HTML5标准的定稿,使人们对HTML5技术燃起了更大的热情,HTML5是唯一通吃PC端、移动端(iOS、Android、WindowsPhone)的跨平台语言。由此带来很多革命性的改变,比如降低创业成本、降低技术门槛,借助HTML5技术,Hybrid混合模式也逐渐被认可。但是,你真的了解HybridAPP吗

2014-11-28 10:31:07

Hybrid APP
真的了解 SQL 注入
黑客通过将恶意的SQL查询或者添加语句插入到应用的输入参数中,然后在后台SQL服务器上解析执行进行程序攻击。那黑客具体是如何将恶意的SQL脚本进行植入到系统中,从而达到攻击的目的呢

2021-01-15 07:44:21

SQL注入 攻击 黑客
神器 Logging,真的了解
logging是python标准模块,用于记录和处理程序中的日志。功能很强大,官方文档很详细,网上也有大量的说明和教程,但是对很多初次接触的同学来说,存在一些障碍。

2021-11-09 09:48:13

Logging python 模块
真的了解package.json
package.json​是Node.js​开发中使用的重要元数据文件。它有助于管理依赖关系、自动化任务并配置项目。该文件包含了项目名称、版本号、作者、许可证、依赖关系等基本信息。

2024-02-02 08:50:20

Node.js 元数据 自动化
并发编程:真的了解FutureTask
FutureTask就是一个实现了一套获取返回值机制的任务体,请注意它依然是任务体,自身不能执行任务,依然需要借助Thread执行。

2023-10-24 08:53:24

FutureTas 并发编程
真的了解ELT和ETL
ELT和ETL都有各自的应用场景,可以说现在大数据环境下,很多已经是ELT架构了,所以这也是我近几年一直不看好很多厂商在推“拖拉拽”的ETL工具或者平台,未来肯定是需要一种通用语言来实现所有的ELT过程。

2022-03-14 07:53:27

ELT ETL 大数据
真的了解云安全威胁
云计算让企业IT心神不宁。Reavis:对于大型业务来说,安全需求确实是问题法规遵从问题,在云端有所进步。

2012-05-31 09:56:54

云安全
JavaScript基础——真的了解JavaScript
做为前端开发,你是否问过自己或者思考过什么是JavaScript吗JavaScript有什么特点小编认为,在你想深入一门语言,必须要清楚理解这门语言有什么特点和其中背后的运行机制,这是学好一门语言的基础。

2019-11-06 09:52:01

JavaScript 单线程 非阻塞
真的了解Android密钥库
Android的密钥库已经推出很多年了,它为应用开发者提供了一种使用加密密钥进行身份验证和加密的方式。

2017-10-18 22:01:12

真的了解实时计算
实时计算是什么?你了解实时计算吗?

2015-07-31 10:35:18

实时计算
灵魂拷问,真的了解DNS
DNS是域名系统的简称,它是一种用于将域名和IP地址相互映射的协议。简单地说,它是一种用于将人类可读的域名(例如www.google.com)转换为浏览器可以理解的IP地址(例如142.251.43.14)的方法。这样,用户就可以通过输入域名来访问网站,而无需记住网站的IP地址。

2022-12-12 08:46:11

真的了解Java类加载机制
自定义类加载器是一种非常有用的工具,可以在某些特殊场景下实现类的隔离和动态加载。虽然自定义类加载器的使用比较复杂,但只要掌握了其原理和使用方法,就可以为我们的应用程序带来更多的可能性。

2023-05-10 11:07:18

玻璃钢生产厂家民权玻璃钢雕塑厂家舟山玻璃钢人物雕塑厂家价格商场周年庆大门美陈云南供应玻璃钢雕塑玻璃钢人物大型园林雕塑生产厂家广州黄埔商场美陈浙江商场创意商业美陈品牌上海玻璃钢雕塑制作厂滁州公园玻璃钢雕塑销售电话感应玻璃钢雕塑多彩玻璃钢雕塑订做价格欧式玻璃钢卡通雕塑图片银川玻璃钢广场雕塑南通玻璃钢雕塑联系方式贵州玻璃钢仿真水果雕塑安徽省玻璃钢雕塑怎么选安徽步行街玻璃钢雕塑批发玻璃钢猪年雕塑厂商场游玩美陈嘉峪关玻璃钢雕塑报价普宁玻璃钢雕塑厂家亳州学校玻璃钢雕塑广东玻璃钢牛动物雕塑艺术摆件商场美陈景观商场美陈互动产品长治玻璃钢动物雕塑成都成品玻璃钢造型雕塑昆明玻璃钢雕塑批量定做玻璃钢雕塑结实吗奥特曼玻璃钢雕塑香港通过《维护国家安全条例》两大学生合买彩票中奖一人不认账让美丽中国“从细节出发”19岁小伙救下5人后溺亡 多方发声单亲妈妈陷入热恋 14岁儿子报警汪小菲曝离婚始末遭遇山火的松茸之乡雅江山火三名扑火人员牺牲系谣言何赛飞追着代拍打萧美琴窜访捷克 外交部回应卫健委通报少年有偿捐血浆16次猝死手机成瘾是影响睡眠质量重要因素高校汽车撞人致3死16伤 司机系学生315晚会后胖东来又人满为患了小米汽车超级工厂正式揭幕中国拥有亿元资产的家庭达13.3万户周杰伦一审败诉网易男孩8年未见母亲被告知被遗忘许家印被限制高消费饲养员用铁锨驱打大熊猫被辞退男子被猫抓伤后确诊“猫抓病”特朗普无法缴纳4.54亿美元罚金倪萍分享减重40斤方法联合利华开始重组张家界的山上“长”满了韩国人?张立群任西安交通大学校长杨倩无缘巴黎奥运“重生之我在北大当嫡校长”黑马情侣提车了专访95后高颜值猪保姆考生莫言也上北大硕士复试名单了网友洛杉矶偶遇贾玲专家建议不必谈骨泥色变沉迷短剧的人就像掉进了杀猪盘奥巴马现身唐宁街 黑色着装引猜测七年后宇文玥被薅头发捞上岸事业单位女子向同事水杯投不明物质凯特王妃现身!外出购物视频曝光河南驻马店通报西平中学跳楼事件王树国卸任西安交大校长 师生送别恒大被罚41.75亿到底怎么缴男子被流浪猫绊倒 投喂者赔24万房客欠租失踪 房东直发愁西双版纳热带植物园回应蜉蝣大爆发钱人豪晒法院裁定实锤抄袭外国人感慨凌晨的中国很安全胖东来员工每周单休无小长假白宫:哈马斯三号人物被杀测试车高速逃费 小米:已补缴老人退休金被冒领16年 金额超20万

玻璃钢生产厂家 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化