12.SpringBoot学习笔记

\@Value获取值和\@ConfigurationProperties获取值比较

Posted by Chen Xingxu on June 20, 2020

SpringBoot学习笔记(十二)–@Value获取值和@ConfigurationProperties获取值比较

@Value

使用 @Value 类似于

<bean class="Person">
   <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL}">            </property>
<bean/>  

使用示例

package demo.yangxu.springboot.bean;
@Component
public class Person {
    //${key}从环境变量、配置文件中获取值
    @Value("${person.last-name}")
    private String lastName;
    //#{SpEL}: Spring Expression Language
    @Value("#{11*2}")
    private Integer age;
    //字面量
    @Value("true")
    private Boolean boss;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

运行结果

Person{lastName='小明', age=22, boss=true, birth=null, maps=null, lists=null, dog=null}

松散绑定(松散语法)

@Value 不支持松散绑定,如果在 @Value 中使用松散语法

@Value("${person.lastName}")
private String lastName;
person.last-name=小明

会出现以下报错

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'person': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'person.lastName' in value "${person.lastName}"

SpEL (Spring Expression Language)

@Value 支持 SpEL

@Value("#{11*2}")
private Integer age;

运行结果

age=22

JSR303 数据校验

@Value 不支持 JSR303 数据校验

添加 @Validated 与 @Email 校验

package demo.yangxu.springboot.bean;
@Component
@Validated
public class Person {
    @Email
    @Value("${person.last-name}")
    private String lastName;
    private Integer age;
    private Boolean boss;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}

运行结果为

Person{lastName='小明', age=null, boss=null, birth=null, maps=null, lists=null, dog=null}

数据校验不起作用。

复杂类型封装

@Value 不支持复杂类型封装

在 @Value 中使用复杂类型封装

package demo.yangxu.springboot.bean;
@Component
public class Person {
    private String lastName;
    private Integer age;
    private Boolean boss;
    private Date birth;
    
    @Value("${person.maps}")
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;

}

运行结果为

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'person': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'person.maps' in value "${person.maps}"

@ConfigurationProperties

不论配置文件是 yml,还是 properties,他们都能获取到值。

松散绑定(松散语法)

@ConfigurationProperties 支持松散绑定

package demo.yangxu.springboot.bean;
@Component
@ConfigurationProperties(prefix="person")
public class Person {
    private String lastName;
    private Integer age;
    private Boolean boss;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}
person.last-name=小明
person.age=18
person.birth=2020/6/6
person.boss=false
person.maps.k1=v1
person.maps.k2=14
person.lists=a,b,c
person.dog.name=小狗
person.dog.age=3

运行结果

Person{lastName='小明', age=18, boss=false, birth=Sat Jun 06 00:00:00 CST 2020, maps={k1=v1, k2=14}, lists=[a, b, c], dog=Dog{name='小狗', age=3}}

SpEL (Spring Expression Language)

@ConfigurationProperties 不支持 SpEL

package demo.yangxu.springboot.bean;
@Component
@ConfigurationProperties(prefix="person")
public class Person {
    private String lastName;
    private Integer age;
    private Boolean boss;
    private Date birth;
    private Map<String,Object> maps;
    private List<Object> lists;
    private Dog dog;
}
person.age=#{11*2}

运行后会报错

***************************
APPLICATION FAILED TO START
***************************
Description:
Failed to bind properties under 'person.age' to java.lang.Integer:
    Property: person.age
    Value: #{11*2}
    Origin: class path resource [application.properties]:4:12
    Reason: failed to convert java.lang.String to java.lang.Integer
Action:
Update your application's configuration

JSR303 数据校验

@ConfigurationProperties 支持 JSR303 数据校验

Spirng Boot 升级到 2.3 之后,需要自行添加以下依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

以下是官方的说法

As of #19550, Web and WebFlux starters do not depend on the validation starter by default anymore. If your application is using validation features, for example you find that javax.validation.* imports are not being resolved, you’ll need to add the starter yourself.

来源:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#validation-starter-no-longer-included-in-web-starters

添加 @Validated 与 @Email 校验

package demo.yangxu.springboot.bean;
@Component
@ConfigurationProperties(prefix="person")
@Validated
public class Person {
    @Email
    private String lastName;
}

运行结果为

***************************
APPLICATION FAILED TO START
***************************
Description:
Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'person' to demo.yangxu.springboot.bean.Person failed:
    Property: person.lastName
    Value: 小明
    Origin: class path resource [application.properties]:3:18
    Reason: 不是一个合法的电子邮件地址
Action:
Update your application's configuration

复杂类型封装

@ConfigurationProperties 支持复杂类型封装

实例

Controller

package demo.yangxu.springboot.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @Value("${person.last-name}")
    private String name;

    @RequestMapping("/sayHello")
    public String sayHello(){
        return "Hello, "+ name;
    }
}

运行结果

@Value 和 @ConfigurationProperties 的使用场景

只在某个业务逻辑中,获取一下配置文件中的某项值,使用 @Value。

若专门编写了一个 javaBean 来和配置文件进行映射,就直接使用 @ConfigurationProperties。

也可以根据实际情况,两种方式结合着用。