📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍
文章目录
- 写在前面的话
- SB 读取配置的方式
- @Value 知识扩展
- 三种方式用法
- 集合与Map注入
- 配合 @PropertySource
- 使用的注意事项
- 源码简单分析
- 总结陈词
写在前面的话
Java 程序猿在面向 SpringBoot 开发的时候,读取配置文件信息是一个日常需求,@Value 应该屡见不鲜了,这边对知识做一个汇总,方便查阅。
Tips:近期在更新
程序猿入职必会
系列(还在进行中),先更换一个知识点,调剂一下,每天都有新东西。
系列文章:
《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》
《程序猿入职必会(2) · 搭建具备前端展示效果的 Vue》
SB 读取配置的方式
先看一下基础知识,SpringBoot 开发中,需要读取application.yml
或Nacos
上的配置信息,可以使用下面两种方式。
方式一,使用@Value读取
配置属性,使用@Value(“${my.name}”)方式注入成员变量,@Value是实现把配置文件的单个属性的提取。
属性若不存在,启动时候就会报错,如下所示:
为防止这种情况,可以指定默认值,例如:
@Value("${system.defaultReply:不能识别的信息}")
private String defaultReply;
@Value("${sql.maxRow:1000}")
private String maxRow;
//#{SPEL} Spring表达式
@Value("#{11*2}")
// 字面量
@Value("true")
Tips:添加上冒号代表后面是默认值,冒号后面是空的代表空字符串,推荐都加上,否则生产环境差异会导致异常。。
方式二,使用 @ConfigurationProperties 绑定实体
@Value 仅适合单个属性的情况,如果属性很多建议用绑定实体的方式。
@ConfigurationProperties可以实现把配置文件的某前缀开始的key自动映射为实体的初值。
1、添加相应的配置文件信息
ali:
oss:
accessKeyId: LTAI4FhYdxC7YY8RR6shfXjk
accessKeySecret: LmVvWUJCQzdQpJyX621Xnf43GasQDO
bucketName: cjwmy1013
endPoint: oss-cn-beijing.aliyuncs.com
fileHost: https://cjwmy1013.oss-cn-beijing.aliyuncs.com/
2、新建一个实体,和配置文件对应,如下:
@Component
@ConfigurationProperties(prefix = "ali.oss")
@Data
public class AliOSSProperties {
private String accessKeyId;
private String accessKeySecret;
private String endPoint;
private String bucketName;
private String fileHost;
}
3、注入实体使用。
@Autowired
private AliOSSProperties aliOss;
4、引入 configuration-processor 依赖,这样绑定后可以有提示,也可以跳转,如下:
<!-- 配置文件对应 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
补充,配置文件和属性不匹配也不会报错,需要校验,可以添加@Validated和@NotNull注解,如下:
@ConfigurationProperties(prefix = "author")
@Validated
@Component
public class AuthorBean {
@NotNull
private String name;
}
@Value 知识扩展
@Value属于 Spring 的注解,在spring-beans包下,可以在字段、方法参数、构造函数参数等上面使用,通常用于属性注入,支持 SpEL 表达式来注入值,同时也支持属性占位符注入值。
三种方式用法
1、配置文件读取值
这个也是最常见的,从配置文件读取值,即application.yml 或 application.properties
。
和前面介绍的一样,建议添加上一个默认值,否则配置不存在会报错。
@Value("${spring.application.name:}")
private String name;
2、直接赋值
直接注入字符串,有点类似于直接给属性赋值一样,感觉多此一举,实际开发当中这种应用场景非常少。
@Value("战神")
private String name;
3、SpEl 表达式赋值
#{} 方式表示 SpEl 表达式,通常用来注入Bean对象。
还有很多强大的用法,可以自行掌握。
@Component
public class MyService {
@Value("#{myBean}")
private MyBean myBean;
}
@Component
public class MyBean {
// ...
}
集合与Map注入
test:
array: aaa,bbb,ccc
map: '{"name": "zhangsan", "sex": "male"}'
// 数组
@Value("${test.array:}")
private String[] array1;
// 集合
@Value("${test.array:}")
private List<String> list1;
// Map
@Value("#{${test.map}}")
private Map<String,String> map1;
配合 @PropertySource
Springboot 默认读取的都是 application.yml 或 application.properties,但是有时候我们想把一些配置给独立起来,这时候可以采用@PropertySource。
@Component
@PropertySource(value = "demo.properties")
public class ReadByProperty {
@Value("${demo.name}")
private String name;
}
使用的注意事项
使用@Value前提:
1、不能直接作用于静态变量(static);
2、不能直接作用于常量(final);
3、不能在非注册的类中使用(类需要被注册在spring上下文中,如用@Service,@RestController,@Component等);
4、使用这个类时,只能通过依赖注入的方式,用new的方式是不会自动注入这些配置的。
静态变量注入:
使用 @Value 注解是不允许在 static 变量注入的,包括 get 方法也是,直接会获取 null 值。
原因很简单,@Value 围绕的是注入到spring容器当中的这个单例对象,而 static 是类变量,所以肯定不可以的。可以理解为 类变量初始化优先于 Spring 对象注入,所以他无法注入进去。
解决方案可以通过方法注入,或者通过@PostConstruct等初始化逻辑注入,总之就是简洁注入。
@Configuration
public class MyConfig {
public static String name;
@Value("${spring.application.name}")
public void initName(String param) {
name = param;
}
}
@Configuration
public class MyConfig {
public static String name;
@Value("${spring.application.name}")
private String param;
@PostConstruct
public void init(){
name = param;
}
}
源码简单分析
以下面代码为例,值是怎么设置进去的呢?
@Value("${spring.application.name:}")
private String authorName;
@Value 实际上是通过 org.springframework.beans.factory.config.BeanPostProcessor 来执行的,实际负责做事的是其实现类 AutowiredAnnotationBeanPostProcessor,它负责检查是否有这个注解的存在。
看一下源码,追踪入口是 AutowiredAnnotationBeanPostProcessor#AutowiredFieldElement#inject
(这里九曲十八弯,过程省略)DefaultListableBeanFactory 当中的 doResolveDependency 方法,通过表达式得到真正的值,不同方式的获取逻辑略有差异。
最后将得到的值,通过反射Field的set赋值。
Tips:由于本篇文章不是专门的源码分析篇,简单介绍一下定位方式,源码还是要自己动手看来得真切。
总结陈词
此篇文章介绍了@Value
在项目中得常见用法,仅供学习参考。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。