• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Spring @Value的注解使用和原理

武飞扬头像
搬山道猿
帮助1

介绍

@Value注解在Spring开发中是一个使用很频繁的注解,在项目开发中,我们通常需要读取配置文件中的一些信息,对于SpringBoot项目,我们一般从yml文件中读取,如果我们自定义了配置文件,那么就可以配合@PropertySource注解来获取配置文件的配置项,当然,@Value不单单能读取配置文件,还能读取系统属性,还可以读取其他bean的属性,本章就来详细介绍@Value注解的使用和对源码进行分析。

使用

如下我们对value的使用进行详细介绍,value可以注入配置文件的属性,注入其它bean的属性,注册spring中自己实现的一些属性,比如操作系统信息。

属性类

MyProperties是一个bean,里面定义了一些属性,一般在项目中,如果需要全局使用某个配置信息,我们通常会定义一个属性类,然后在需要使用的地方直接注入,比如系统中我们需要存储大量的文件,文件是存储在文件服务器上面,数据库只存储文件所在文件系统的目录路径,而不会存储具体的ip地址,如果我们存储了能直接访问文件的链接,后续如果进行文件迁移,那么这些链接就不好处理,所以应该只存储文件在文件服务器的目录路径,那么返回给前端显示的时候,再获取文件服务器地址进行拼接就可以。

  1.  
    /**
  2.  
    * 功能说明: 属性配置类
  3.  
    * <p>
  4.  
    * Original @Author: steakliu-刘牌, 2023-04-27 10:08
  5.  
    * <p>
  6.  
    * Copyright (C)2020-2022 steakliu All rights reserved.
  7.  
    */
  8.  
    @Data
  9.  
    @Component
  10.  
    public class MyProperties {
  11.  
    /**
  12.  
    * 注入其他bean的属性
  13.  
    */
  14.  
    @Value("#{valueBean.username}")
  15.  
    private String username;
  16.  
    /**
  17.  
    * 注入配置文件属性
  18.  
    */
  19.  
    @Value("${minio.url}")
  20.  
    private String minioUrl;
  21.  
    /**
  22.  
    * 注入操作系统属性
  23.  
    */
  24.  
    @Value("#{systemProperties['os.name']}")
  25.  
    private String os;
  26.  
    }
  27.  
    复制代码
学新通

配置类

配置类主要就是使用@PropertySource注解来获取配置配置文件的属性。

  1.  
    @Configuration
  2.  
    @PropertySource("classpath:minio.properties")
  3.  
    public class ValueConfiguration {
  4.  
     
  5.  
    }
  6.  
    复制代码

配置文件minio.properties

配置文件里面就放了一个minio的地址

  1.  
    minio.url=http://www.gss.cn/
  2.  
    复制代码

通过上面的配置,我们就可以在需要使用minio地址的地方注入MyProperties这bean就可以,可能有些人会觉得麻烦,还需要注入bean,直接写在一个常量里面不就行,其实不然,这样做更加的规范,做到了配置和代码的分离,不同的环境的地址不同,或者发生文件迁移,就可以直接修改配置文件,还有配置文件可以写入注册中心,可以更具一定的策略进行修改后刷新,@Value注解只是获取配置文件属性的一种方式,在SpringBoot中,@ConfigurationProperties使用起来也很方便。

原理解析

下面对@Value的原理进行解析,因为我们使用@Value大多时候是放在字段上面,并且要使用在一个Bean中,那么我们知道bean在实例化的时候需要进行属性填充,就会对这些属性进行赋值,所以下面就从实例化bean开始对@Value进行解析。

解析属性

我们从AbstractAutowireCapableBeanFactory类这里开始,在类中进入doCreateBean()方法,然后进入applyMergedBeanDefinitionPostProcessors,最终会进入AutowiredAnnotationBeanPostProcessor后置处理器中,@Autowired,@Value,@Inject都是它进行处理,下面我们看最主要的部分buildAutowiringMetadata

如下代码,Spring使用反射获取字段,如果是字段被static修饰,那么在此处是会被排除,使用的是Modifier.isStatic(int mod)方法,通过反射拿到字段后,组装后加入一个名字为injectionMetadataCache的Map中,后面属性填充会直接从这个缓存中获取。

  1.  
    private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
  2.  
    List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
  3.  
    Class<?> targetClass = clazz;
  4.  
    do {
  5.  
    final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
  6.  
    ReflectionUtils.doWithLocalFields(targetClass, field -> {
  7.  
    MergedAnnotation<?> ann = findAutowiredAnnotation(field);
  8.  
    if (ann != null) {
  9.  
    if (Modifier.isStatic(field.getModifiers())) {
  10.  
    if (logger.isInfoEnabled()) {
  11.  
    logger.info("Autowired annotation is not supported on static fields: " field);
  12.  
    }
  13.  
    return;
  14.  
    }
  15.  
    boolean required = determineRequiredStatus(ann);
  16.  
    currElements.add(new AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement(field, required));
  17.  
    }
  18.  
    });
  19.  
    }
  20.  
    while (targetClass != null && targetClass != Object.class);
  21.  
     
  22.  
    return InjectionMetadata.forElements(elements, clazz);
  23.  
    }
  24.  
    复制代码
学新通

属性填充

属性填充阶段进入的是对bean的属性进行赋值,这是Spring生命周期中很重要的一个阶段,方法是populateBean,也在AbstractAutowireCapableBeanFactory类中,接着会调用AutowiredAnnotationBeanPostProcessor中的postProcessProperties方法,然后往下继续执行,核心代码如下,如下就是给每个属性赋值,往下执行还有很多逻辑处理,如解析@Value注解的表达式,然后根据表达式去获取对应的值等,就不深入去解析。

  1.  
    protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
  2.  
    Field field = (Field) this.member;
  3.  
    Object value;
  4.  
    if (this.cached) {
  5.  
    try {
  6.  
    value = resolvedCachedArgument(beanName, this.cachedFieldValue);
  7.  
    } catch (NoSuchBeanDefinitionException ex) {
  8.  
    // Unexpected removal of target bean for cached argument -> re-resolve
  9.  
    value = resolveFieldValue(field, bean, beanName);
  10.  
    }
  11.  
    } else {
  12.  
    value = resolveFieldValue(field, bean, beanName);
  13.  
    }
  14.  
    if (value != null) {
  15.  
    ReflectionUtils.makeAccessible(field);
  16.  
    field.set(bean, value);
  17.  
    }
  18.  
    }
  19.  
    复制代码
学新通

总结

上面对@Value的使用和原理进行了介绍,其实@Value,@Autowired,@Resource,@Inject这几个的作用都是进行属性装配,只不过他们的方式各有不同,@Value,@Autowired,@Inject是使用AutowiredAnnotationBeanPostProcessor后置处理器进行处理,@Resource则使用CommonAnnotationBeanPostProcessor后置处理器进行处理。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgjbbci
系列文章
更多 icon
同类精品
更多 icon
继续加载