微信公众号:一个优秀的废人。如有问题,请后台留言,反正我也不会听。

前言

最近有跳槽的想法,所以故意复习了下 SpringBoot 的相关知识,复习得比较细。其中有些,我感觉是以前忽略掉的东西,比如 @Value 和 @ConfigurationProperties 的区别 。

如何使用

定义两个对象,一个学生对象,对应着一个老师对象,代码如下:

  • @ConfigurationProperties
  1. 学生类
@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 方法
}
  1. 老师类
public class Teacher {

    private String name;

    private Integer age;

    private String gender;

    //注意,为了测试必须重写 toString 和 get,set 方法
}
  1. 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootValConproDemoApplicationTests { @Autowired
private Student student; @Test
public void contextLoads() {
// 这里为了方便,但工作中千万不能用 System.out
System.out.println(student.toString());
}
}
  1. 输出结果
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}

  1. 学生类
@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 方法
}
  1. 测试结果
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 的区别的更多相关文章

  1. Spring Boot2 系列教程(十九)Spring Boot 整合 JdbcTemplate

    在 Java 领域,数据持久化有几个常见的方案,有 Spring 自带的 JdbcTemplate .有 MyBatis,还有 JPA,在这些方案中,最简单的就是 Spring 自带的 JdbcTem ...

  2. Spring Boot2 系列教程(十)Spring Boot 整合 Freemarker

    今天来聊聊 Spring Boot 整合 Freemarker. Freemarker 简介 这是一个相当老牌的开源的免费的模版引擎.通过 Freemarker 模版,我们可以将数据渲染成 HTML ...

  3. Spring Boot2 系列教程 (十八) | 整合 MongoDB

    微信公众号:一个优秀的废人.如有问题,请后台留言,反正我也不会听. 前言 如题,今天介绍下 SpringBoot 是如何整合 MongoDB 的. MongoDB 简介 MongoDB 是由 C++ ...

  4. Spring Boot2 系列教程 (十四) | 统一异常处理

    如题,今天介绍 SpringBoot 是如何统一处理全局异常的.SpringBoot 中的全局异常处理主要起作用的两个注解是 @ControllerAdvice 和 @ExceptionHandler ...

  5. Spring Boot2 系列教程 (十二) | 整合 thymeleaf

    前言 如题,今天介绍 Thymeleaf ,并整合 Thymeleaf 开发一个简陋版的学生信息管理系统. SpringBoot 提供了大量模板引擎,包含 Freemarker.Groovy.Thym ...

  6. Spring Boot2 系列教程 (十) | 实现声明式事务

    前言 如题,今天介绍 SpringBoot 的 声明式事务. Spring 的事务机制 所有的数据访问技术都有事务处理机制,这些技术提供了 API 用于开启事务.提交事务来完成数据操作,或者在发生错误 ...

  7. Spring Boot2 系列教程(十二)@ControllerAdvice 的三种使用场景

    严格来说,本文并不算是 Spring Boot 中的知识点,但是很多学过 SpringMVC 的小伙伴,对于 @ControllerAdvice 却并不熟悉,Spring Boot 和 SpringM ...

  8. Spring Boot2 系列教程(十四)CORS 解决跨域问题

    今天和小伙伴们来聊一聊通过CORS解决跨域问题. 同源策略 很多人对跨域有一种误解,以为这是前端的事,和后端没关系,其实不是这样的,说到跨域,就不得不说说浏览器的同源策略. 同源策略是由 Netsca ...

  9. Spring Boot2 系列教程(十五)定义系统启动任务的两种方式

    在 Servlet/Jsp 项目中,如果涉及到系统任务,例如在项目启动阶段要做一些数据初始化操作,这些操作有一个共同的特点,只在项目启动时进行,以后都不再执行,这里,容易想到web基础中的三大组件( ...

  10. Spring Boot2 系列教程(十六)定时任务的两种实现方式

    在 Spring + SpringMVC 环境中,一般来说,要实现定时任务,我们有两中方案,一种是使用 Spring 自带的定时任务处理器 @Scheduled 注解,另一种就是使用第三方框架 Qua ...

随机推荐

  1. 2019-1-29-C#-Task.Run-和-Task.Factory.StartNew-区别

    title author date CreateTime categories C# Task.Run 和 Task.Factory.StartNew 区别 lindexi 2019-01-29 16 ...

  2. 日历价差(calendar spread)

    日历价差(calendar spread) 是指投资者买进到期日较远的期权 (简称远期期权),同时又卖出相同行权价格.相同数量但到期日较近的期权(简称近期期权),赚取两个不同期权隐含波动率的差价或者其 ...

  3. [C++] WinAES的问题

    WinAES是个不错的windows CAPI封装. 如果C++程序需要和java的程序进行aes加解密通讯,那么WinAES的代码是有问题的. java的aes代码缺省不会设置IV而且采用ECB模式 ...

  4. 2018-3-7-VisualStudio-csproj-添加-ItemGroup-的-Service-

    title author date CreateTime categories VisualStudio csproj 添加 ItemGroup 的 Service lindexi 2018-3-7 ...

  5. Educational Codeforces Round 54 (Rated for Div. 2) D Edge Deletion (SPFA + bfs)

    题目大意:给定你一个包含n个点m条边的无向图,现在最多在图中保留k条边,问怎么删除多的边,使得图中良好的节点数最多,求出保留在图中的边的数量和编号. 良好的节点定义为:删除某条边后该点到点1的最短距离 ...

  6. 读《Effect Java中文版》

    读<Effect Java中文版> 译者序 序 前言 第1章引言 1   第2章创建和销毁对象 4 第1条:考虑用静态工厂方法代替构造函数 4 第2条:使用私有构造函数强化singleto ...

  7. linux 一个使用整页的 scull: scullp

    为了真实地测试页分配, 我们已随其他例子代码发布了 scullp 模块. 它是一个简化的 scull, 就像前面介绍过的 scullc. scullp 分配的内存量子是整页或者页集合: scullp_ ...

  8. 【Linux】nl笔记

    nl命令在linux系统中用来计算文件中行号.nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等 ...

  9. 常用数据库连接池 (DBCP、c3p0、Druid) 配置说明(转)

    1. 引言 1.1 定义 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库 ...

  10. Web_telnet 通过JAVA实现网页上面的命令行远程工具 以及对于流的理解

    前言 这几天研究了一下如何在web页面上通过telnet 连接一个远程的机器,通过命令行进行控制,让然,B/S架构的项目,如果直接通过 浏览器是无法和远程机器进行通信的,我们就得借助后端来帮助实现这样 ...