Spring Boot2 系列教程(十九) | @Value 和 @ConfigurationProperties 的区别
微信公众号:一个优秀的废人。如有问题,请后台留言,反正我也不会听。
前言
最近有跳槽的想法,所以故意复习了下 SpringBoot 的相关知识,复习得比较细。其中有些,我感觉是以前忽略掉的东西,比如 @Value 和 @ConfigurationProperties 的区别 。
如何使用
定义两个对象,一个学生对象,对应着一个老师对象,代码如下:
- @ConfigurationProperties
- 学生类
@Component
@ConfigurationProperties(prefix = "student") // 指定配置文件中的 student 属性与这个 bean绑定
public class Student {
private String firstName;
private String lastName;
private Integer age;
private String gender;
private String city;
private Teacher teacher;
private List<String> hobbys;
private Map<String,Integer> scores;
//注意,为了测试必须重写 toString 和 get,set 方法
}
- 老师类
public class Teacher {
private String name;
private Integer age;
private String gender;
//注意,为了测试必须重写 toString 和 get,set 方法
}
- 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootValConproDemoApplicationTests {
@Autowired
private Student student;
@Test
public void contextLoads() {
// 这里为了方便,但工作中千万不能用 System.out
System.out.println(student.toString());
}
}
- 输出结果
Student{firstName='陈', lastName='一个优秀的废人', age=24, gender='男', city='广州', teacher=Teacher{name='eses', age=24, gender='女'}, hobbys=[篮球, 羽毛球, 兵兵球], scores={java=100, Python=99, C=99}}
- @Value
@Value 支持三种取值方式,分别是 字面量、${key}从环境变量、配置文件中获取值以及 #{SpEL}
- 学生类
@Component
//@ConfigurationProperties(prefix = "student") // 指定配置文件中的 student 属性与这个 bean绑定
public class Student {
/**
* <bean class="Student">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
@Value("陈") // 字面量
private String firstName;
@Value("${student.lastName}") // 从环境变量、配置文件中获取值
private String lastName;
@Value("#{12*2}") // #{SpEL}
private Integer age;
private String gender;
private String city;
private Teacher teacher;
private List<String> hobbys;
private Map<String,Integer> scores;
//注意,为了测试必须重写 toString 和 get,set 方法
}
- 测试结果
Student{firstName='陈', lastName='一个优秀的废人', age=24, gender='null', city='null', teacher=null, hobbys=null, scores=null}
区别
| 二者区别 | @ConfigurationProperties | @Value |
|---|---|---|
| 功能 | 批量注入配置文件中的属性 | 一个个指定 |
| 松散绑定(松散语法) | 支持 | 不支持 |
| SpEL | 不支持 | 支持 |
| JSR303数据校验 | 支持 | 不支持 |
| 复杂类型封装 | 支持 | 不支持 |
从上表可以看见,@ConfigurationProperties 和 @Value 主要有 5 个不同,其中第一个功能上的不同,上面已经演示过。下面我来介绍下剩下的 4 个不同。
松散语法
松散语法的意思就是一个属性在配置文件中可以有多个属性名,举个栗子:学生类当中的 firstName 属性,在配置文件中可以叫 firstName、first-name、first_name 以及 FIRST_NAME。 而 @ConfigurationProperties 是支持这种命名的,@Value 不支持。下面以 firstName 为例,测试一下。如下代码:
- @ConfigurationProperties
学生类的 firstName 属性在 yml 文件中被定义为 first_name:
student:
first_name: 陈 # 学生类的 firstName 属性在 yml 文件中被定义为 first_name
lastName: 一个优秀的废人
age: 24
gender: 男
city: 广州
teacher: {name: eses,age: 24,gender: 女}
hobbys: [篮球,羽毛球,兵兵球]
scores: {java: 100,Python: 99,C++: 99}
学生类:
@Component
@ConfigurationProperties(prefix = "student") // 指定配置文件中的 student 属性与这个 bean绑定
public class Student {
/**
* <bean class="Student">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
//@Value("陈") // 字面量
private String firstName;
//@Value("${student.lastName}") // 从环境变量、配置文件中获取值
private String lastName;
//@Value("#{12*2}") // #{SpEL}
private Integer age;
private String gender;
private String city;
private Teacher teacher;
private List<String> hobbys;
private Map<String,Integer> scores;
//注意,为了测试必须重写 toString 和 get,set 方法
}
测试结果:
Student{firstName='陈', lastName='一个优秀的废人', age=24, gender='男', city='广州', teacher=Teacher{name='eses', age=24, gender='女'}, hobbys=[篮球, 羽毛球, 兵兵球], scores={java=100, Python=99, C=99}}
- @Value
学生类:
@Component
//@ConfigurationProperties(prefix = "student") // 指定配置文件中的 student 属性与这个 bean绑定
public class Student {
/**
* <bean class="Student">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
//@Value("陈") // 字面量
@Value("${student.firstName}")
private String firstName;
//@Value("${student.lastName}") // 从环境变量、配置文件中获取值
private String lastName;
//@Value("#{12*2}") // #{SpEL}
private Integer age;
private String gender;
private String city;
private Teacher teacher;
private List<String> hobbys;
private Map<String,Integer> scores;
//注意,为了测试必须重写 toString 和 get,set 方法
}
测试结果:启动报错,找不到 bean。
从上面两个测试结果可以看出,使用 @ConfigurationProperties 注解时,yml 中的属性名为 last_name 而学生类中的属性为 lastName 但依然能取到值,而使用 @value 时,使用 lastName 确报错了。证明 @ConfigurationProperties 支持松散语法,@value 不支持。
SpEL
SpEL 使用 #{...} 作为定界符 , 所有在大括号中的字符都将被认为是 SpEL , SpEL 为 bean 的属性进行动态赋值提供了便利。
- @Value
如上述介绍 @Value 注解使用方法时,有这样一段代码:
@Value("#{12*2}") // #{SpEL}
private Integer age;
证明 @Value 是支持 SpEL 表达式的。
- @ConfigurationProperties
由于 yml 中的 # 被当成注释看不到效果。所以我们新建一个 application.properties 文件。把 yml 文件内容注释,我们在 properties 文件中把 age 属性写成如下所示:
student.age=#{12*2}
把学生类中的 @ConfigurationProperties 注释打开,注释 @value 注解。运行报错, age 属性匹配异常。
说明 @ConfigurationProperties 不支持 SpEL
JSR303 数据校验
- @Value
加入 @Length 校验:
@Component
@Validated
//@ConfigurationProperties(prefix = "student") // 指定配置文件中的 student 属性与这个 bean绑定
public class Student {
/**
* <bean class="Student">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
//@Value("陈") // 字面量
@Value("${student.first-name}")
@Length(min=5, max=20, message="用户名长度必须在5-20之间")
private String firstName;
//@Value("${student.lastName}") // 从环境变量、配置文件中获取值
private String lastName;
//@Value("#{12*2}") // #{SpEL}
private Integer age;
private String gender;
private String city;
private Teacher teacher;
private List<String> hobbys;
private Map<String,Integer> scores;
}
yaml:
student:
first_name: 陈
测试结果:
Student{firstName='陈', lastName='null', age=null, gender='null', city='null', teacher=null, hobbys=null, scores=null}
yaml 中的 firstname 长度为 1 。而检验规则规定 5-20 依然能取到属性,说明检验不生效,@Value 不支持 JSR303 数据校验
- @ConfigurationProperties
学生类:
@Component
@Validated
@ConfigurationProperties(prefix = "student") // 指定配置文件中的 student 属性与这个 bean绑定
public class Student {
/**
* <bean class="Student">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
//@Value("陈") // 字面量
//@Value("${student.first-name}")
@Length(min=5, max=20, message="用户名长度必须在5-20之间")
private String firstName;
//@Value("${student.lastName}") // 从环境变量、配置文件中获取值
private String lastName;
//@Value("#{12*2}") // #{SpEL}
private Integer age;
private String gender;
private String city;
private Teacher teacher;
private List<String> hobbys;
private Map<String,Integer> scores;
}
测试结果:报错
[firstName],20,5]; default message [用户名长度必须在5-20之间]
校验生效,支持 JSR303 数据校验。
复杂类型封装
复杂类型封装指的是,在对象以及 map (如学生类中的老师类以及 scores map)等属性中,用 @Value 取是取不到值,比如:
@Component
//@Validated
//@ConfigurationProperties(prefix = "student") // 指定配置文件中的 student 属性与这个 bean绑定
public class Student {
/**
* <bean class="Student">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}"></property>
* <bean/>
*/
//@Value("陈") // 字面量
//@Value("${student.first-name}")
//@Length(min=5, max=20, message="用户名长度必须在5-20之间")
private String firstName;
//@Value("${student.lastName}") // 从环境变量、配置文件中获取值
private String lastName;
//@Value("#{12*2}") // #{SpEL}
private Integer age;
private String gender;
private String city;
@Value("${student.teacher}")
private Teacher teacher;
private List<String> hobbys;
@Value("${student.scores}")
private Map<String,Integer> scores;
}
这样取是报错的。而上文介绍 @ConfigurationProperties 和 @Value 的使用方法时已经证实 @ConfigurationProperties 是支持复杂类型封装的。也就是说 yaml 中直接定义 teacher 以及 scores 。 @ConfigurationProperties 依然能取到值。
怎么选用?
如果说,只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用 @Value;比如,假设现在学生类加多一个属性叫 school 那这个属性对于该校所有学生来说都是一样的,但防止我这套系统到了别的学校就用不了了。那我们可以直接在 yml 中给定 school 属性,用 @Value 获取。当然上述只是举个粗暴的例子,实际开发时,school 属性应该是保存在数据库中的。
如果说,专门编写了一个 javaBean 来和配置文件进行映射,我们就直接使用 @ConfigurationProperties。
完整代码
https://github.com/turoDog/Demo/tree/master/springboot_val_conpro_demo
如果觉得对你有帮助,请给个 Star 再走呗,非常感谢。
后语
如果看到这里,喜欢这篇文章的话,请转发、点赞。微信搜索「一个优秀的废人」,欢迎关注。
回复「1024」送你一套完整的 java、python、c++、go、前端、linux、算法、大数据、人工智能、小程序以及英语教程。
回复「电子书」送你 50+ 本 java 电子书。

Spring Boot2 系列教程(十九) | @Value 和 @ConfigurationProperties 的区别的更多相关文章
- Spring Boot2 系列教程(十九)Spring Boot 整合 JdbcTemplate
在 Java 领域,数据持久化有几个常见的方案,有 Spring 自带的 JdbcTemplate .有 MyBatis,还有 JPA,在这些方案中,最简单的就是 Spring 自带的 JdbcTem ...
- Spring Boot2 系列教程(十)Spring Boot 整合 Freemarker
今天来聊聊 Spring Boot 整合 Freemarker. Freemarker 简介 这是一个相当老牌的开源的免费的模版引擎.通过 Freemarker 模版,我们可以将数据渲染成 HTML ...
- Spring Boot2 系列教程 (十八) | 整合 MongoDB
微信公众号:一个优秀的废人.如有问题,请后台留言,反正我也不会听. 前言 如题,今天介绍下 SpringBoot 是如何整合 MongoDB 的. MongoDB 简介 MongoDB 是由 C++ ...
- Spring Boot2 系列教程 (十四) | 统一异常处理
如题,今天介绍 SpringBoot 是如何统一处理全局异常的.SpringBoot 中的全局异常处理主要起作用的两个注解是 @ControllerAdvice 和 @ExceptionHandler ...
- Spring Boot2 系列教程 (十二) | 整合 thymeleaf
前言 如题,今天介绍 Thymeleaf ,并整合 Thymeleaf 开发一个简陋版的学生信息管理系统. SpringBoot 提供了大量模板引擎,包含 Freemarker.Groovy.Thym ...
- Spring Boot2 系列教程 (十) | 实现声明式事务
前言 如题,今天介绍 SpringBoot 的 声明式事务. Spring 的事务机制 所有的数据访问技术都有事务处理机制,这些技术提供了 API 用于开启事务.提交事务来完成数据操作,或者在发生错误 ...
- Spring Boot2 系列教程(十二)@ControllerAdvice 的三种使用场景
严格来说,本文并不算是 Spring Boot 中的知识点,但是很多学过 SpringMVC 的小伙伴,对于 @ControllerAdvice 却并不熟悉,Spring Boot 和 SpringM ...
- Spring Boot2 系列教程(十四)CORS 解决跨域问题
今天和小伙伴们来聊一聊通过CORS解决跨域问题. 同源策略 很多人对跨域有一种误解,以为这是前端的事,和后端没关系,其实不是这样的,说到跨域,就不得不说说浏览器的同源策略. 同源策略是由 Netsca ...
- Spring Boot2 系列教程(十五)定义系统启动任务的两种方式
在 Servlet/Jsp 项目中,如果涉及到系统任务,例如在项目启动阶段要做一些数据初始化操作,这些操作有一个共同的特点,只在项目启动时进行,以后都不再执行,这里,容易想到web基础中的三大组件( ...
- Spring Boot2 系列教程(十六)定时任务的两种实现方式
在 Spring + SpringMVC 环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Qua ...
随机推荐
- Yarn install 报错 Resolving packages... [2/4] Fetching packages... info There appears to be trouble with your network connection. Retrying
1.设置淘宝代理 yarn config set registry 'https://registry.npm.taobao.org' 2.如果网址本地可以打开,说明你本地有代理设置 所以需要按本地的 ...
- 在Vue 中调用数据出现属性不存在的问题
这已经是我在调用数据时趟过几次的坑了,索性记录下来防止后面再犯: 一般我们请求数据来渲染一个页面的时候,请求下来的数据基本上都是数组或是对象,再通过列表循环和插值表达式渲染的页面:在data 中提前声 ...
- 整理了一下angularJs的webpack模板
github地址:https://github.com/qianxiaoning/demo-angularJs1.7.5 欢迎大家star或者fork呀~ 目录结构 src/ components/ ...
- C# “不支持给定路径的格式”异常处理
问题背景 无聊研究了一下怎么发送邮件(包含附件),但发现附带的文件路径除了报错就是报错,不知道为什么. 用了不下好几种方式,比如 var x = "E:\\Git\\cmd\\git.exe ...
- H3C 递归查询
- ZR979B. 【十联测 Day 9】唯一睿酱
ZR979B. [十联测 Day 9]唯一睿酱 题目大意: 给定一个数组\(r_i\),表明对于第\(i\)个数来说,他是\([max(1,i - r_i),min(n,i+r_i)]\)中最大的,求 ...
- linux 操作 I/O 端口
在驱动硬件请求了在它的活动中需要使用的 I/O 端口范围之后, 它必须读且/或写到这些 端口. 为此, 大部分硬件区别 8-位, 16-位, 和 32-位端口. 常常你无法混合它们, 象你 正常使 ...
- gulp插件使用
//引入gulp组件 var gulp=require('gulp'); //创建任务 gulp.task('hello',function(){ console.log('hello'); }); ...
- CachedRowSet 接口
Sun Microsystems 提供的 CachedRowSet 接口的参考实现是一个标准实现.开发人员可以按原样使用此实现.可以扩展它,也可以选择自己编写此接口的实现. CachedRowSet ...
- Service Mesh服务网格清单
Service Mesh服务网格清单 Istio Istio官网 Istio中文官网 Istio开源 无需太多介绍Service Mesh明日之星,扛把子,截止2019.11还有太多问题没解决 复杂性 ...