一文全面深入了解Spring中的@Value注解

简介: 一文全面深入了解Spring中的@Value注解

概述


在项目中大家应该都是用过@Value注解读取配置文件中的值,实际上@Value的功能原比想象中的强大。本文主要针对@Value的注解做一个全面的总结,同时通过源码分析它的实现原理。


@Value注解介绍和使用


介绍


@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
  /**
   * The actual value expression such as <code>#{systemProperties.myProp}</code>
   * or property placeholder such as <code>${my.app.myProp}</code>.
   */
  String value();
}
  • @Value只有一个属性value,可以理解为取值
  • 该注解可以作用于属性字段、方法、入参、其他注解上。


使用场景


该注解主要可以将外部的值注入到Bean中,概括来有以下几种场景:


注入配置文件属性


public class ValueTestBean {
    @Value("${bsfit.user.user-name:alvin}")
    private String userName;
    @Value("${bsfit.user.age}")
    private Integer age;
    private String sex;
    private String userId;
    @Value("${bsfit.user.sex}")
    public void setSex(String sex) {
        this.sex = sex;
    }
    @Autowired
    public void setUserId(@Value("${bsfit.user.userId}") String userId) {
        this.userId = userId;
    }
}

配置如下:

bsfit.user:
user-name: ${person.last-name}
age: 55
userId: E000001
  sex: boy

输出结果:

ValueTestBean(userName=alvinbaf65cc0-3728-42d0-9255-19cad89fcb7a, age=55, sex=boy, userId=E000001)
  • 使用最多的一种场景,从配置文件读取数据,格式:${userName:defaut_value},冒号后面表示如果读取不到userName的值,则用默认值。
  • 不支持松散绑定,也就是说比如配置中写user-id@Value中也必须写user-id


注入SPEL表达式结果


@Value("#{ T(java.lang.Math).random() * 100.0 }")
private double randomNumber; //注入表达式结果


注入系统属性


@Value("#{systemProperties['os.name']}")
 private String systemPropertiesName; // 注入操作系统属性
  • 可以通过System.getProperties()方法获取操作系统有哪些属性


注入其他Bean的属性


@Value("#{person.birth}")
 private Date personBirth;
  • person是其他bean的名称


注入字符串字面量


@Component
public class ValueTestBean {
    @Value("hello")
    private String plainStr;
}
  • 直接写字符串,那么最终plainStr的值就是hello


注入文件资源


@Value("classpath:person.properties")
private Resource resourceFile; // 注入文件资源


注入URL资源


@Value("http://www.baidu.com")
private Resource url; // 注入URL资源


最终全量的输出结果:

image.png


代码地址:

github.com/alvinlkk/sp…


使用注意点


  1. @Value可以使用${xxx}或者#{xxx}, 比如@Value("${user.name:alvin}")获取属性文件中对应的值, 而@Value("#{'Hello World'.concat('!')}")可以写复杂的SPEL表达式。
  2. {...}和#{...}可以混合使用,但是#中可以包含, 中不能包含#。比如正确的使用:`@Value("#{'{server.name}'.split(',')}"),错误的使用:@Value("${#{'HelloWorld'.concat('_')}}")`。
  3. @Value("${user.name}")如果user.name获取不到,启动会报错,我们可以冒号带上默认值@Value("${user.name:alvin}"),这样就不会报错了。
  4. 类型转换报错,如果配置中读取的是String类型字符串,绑定到int类型上,会报错。
  5. 区别于@ConfigrationProperties,@Value不支持松散绑定, 意味着配置文件写的user-id,代码中也要用user-id读取。


源码解析


我们重点关注下最常见的一个场景,从配置文件中获取值是如何实现的。

@Value("${bsfit.user.user-name:alvin}")
 private String userName;


执行主流程


我们看@Value注解的源码,他的注释其实提示了我们是由哪个类来处理的,如下:

image.png

所以我们就优先重点关注这个类AutowiredAnnotationBeanPostProcessor,根据这个类的javadoc,它说明了该类实现了BeanPostProcessor接口,处理通过@Autowird@Value注入属性,我们重点关注在@Value上。

整个属性的注入其实是在Bean的初始化阶段,而AutowiredAnnotationBeanPostProcessor是一个初始化的前后置处理器,所以整个执行流程如下:

image.png

上面就是整个@Value属性的时序图。

  1. 通过调用AbstractAutowireCapableBeanFactorypopulateBean方法在Bean的初始化阶段填充属性值。
  2. populateBean方法中调用了InstantiationAwareBeanPostProcessor接口的postProcessProperties回调方法处理真正的属性填充,InstantiationAwareBeanPostProcessor的实现类很多,其中的AutowiredAnnotationBeanPostProcessor实现是用来处理属性填充的。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    .......
      for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
        // 调用postProcessProperties方法处理属性
                PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
        if (pvsToUse == null) {
          if (filteredPds == null) {
            filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
          }
          pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
          if (pvsToUse == null) {
            return;
          }
        }
        pvs = pvsToUse;
      }
           .......
    }
  1. 下图是AutowiredAnnotationBeanPostProcessor中的postProcessProperties方法,用来处理属性。

image.png

下面是InjectionMetadata类的inject方法,也就是上面调用的下来的。

image.png

接着调用AutowiredFieldElement类的inject方法注入,如下图:

image.png

  1. 最后我们重点关注它是如何解析出值的,我们关注在AutowiredFieldElement#resolveFieldValue()方法。

image.png

接着看DefaultListableBeanFactory#resolveDependency方法。

image.png

DefaultListableBeanFactory#doResolveDependency方法是真正执行变量值获取的方法。

image.png

以上是执行的一个大致主流程,很多细节还是要大家自己debug一步一步去执行。


小结


@Value还是一个使用频率很高的注解,它支持SPEL表达式,功能强大。

目录
相关文章
|
5月前
|
缓存 监控 Java
SpringBoot @Scheduled 注解详解
使用`@Scheduled`注解实现方法周期性执行,支持固定间隔、延迟或Cron表达式触发,基于Spring Task,适用于日志清理、数据同步等定时任务场景。需启用`@EnableScheduling`,注意线程阻塞与分布式重复问题,推荐结合`@Async`异步处理,提升任务调度效率。
858 128
|
4月前
|
XML Java 应用服务中间件
【SpringBoot(一)】Spring的认知、容器功能讲解与自动装配原理的入门,带你熟悉Springboot中基本的注解使用
SpringBoot专栏开篇第一章,讲述认识SpringBoot、Bean容器功能的讲解、自动装配原理的入门,还有其他常用的Springboot注解!如果想要了解SpringBoot,那么就进来看看吧!
547 2
|
5月前
|
Java 测试技术 数据库
使用Spring的@Retryable注解进行自动重试
在现代软件开发中,容错性和弹性至关重要。Spring框架提供的`@Retryable`注解为处理瞬时故障提供了一种声明式、可配置的重试机制,使开发者能够以简洁的方式增强应用的自我恢复能力。本文深入解析了`@Retryable`的使用方法及其参数配置,并结合`@Recover`实现失败回退策略,帮助构建更健壮、可靠的应用程序。
667 1
使用Spring的@Retryable注解进行自动重试
|
5月前
|
XML Java 数据格式
常用SpringBoot注解汇总与用法说明
这些注解的使用和组合是Spring Boot快速开发和微服务实现的基础,通过它们,可以有效地指导Spring容器进行类发现、自动装配、配置、代理和管理等核心功能。开发者应当根据项目实际需求,运用这些注解来优化代码结构和服务逻辑。
420 12
|
5月前
|
传感器 Java 数据库
探索Spring Boot的@Conditional注解的上下文配置
Spring Boot 的 `@Conditional` 注解可根据不同条件动态控制 Bean 的加载,提升应用的灵活性与可配置性。本文深入解析其用法与优势,并结合实例展示如何通过自定义条件类实现环境适配的智能配置。
292 0
探索Spring Boot的@Conditional注解的上下文配置
|
5月前
|
智能设计 Java 测试技术
Spring中最大化@Lazy注解,实现资源高效利用
本文深入探讨了 Spring 框架中的 `@Lazy` 注解,介绍了其在资源管理和性能优化中的作用。通过延迟初始化 Bean,`@Lazy` 可显著提升应用启动速度,合理利用系统资源,并增强对 Bean 生命周期的控制。文章还分析了 `@Lazy` 的工作机制、使用场景、最佳实践以及常见陷阱与解决方案,帮助开发者更高效地构建可扩展、高性能的 Spring 应用程序。
224 0
Spring中最大化@Lazy注解,实现资源高效利用
|
5月前
|
Java 测试技术 编译器
@GrpcService使用注解在 Spring Boot 中开始使用 gRPC
本文介绍了如何在Spring Boot应用中集成gRPC框架,使用`@GrpcService`注解实现高效、可扩展的服务间通信。内容涵盖gRPC与Protocol Buffers的原理、环境配置、服务定义与实现、测试方法等,帮助开发者快速构建高性能的微服务系统。
1070 0
|
Java API Spring
Spring容器如何使用一个注解来指定一个类型为配置类型
Spring容器如何使用一个注解来指定一个类型为配置类型
217 0
|
XML Java 测试技术
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
857 26
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
607 73