配置绑定就是把配置文件中的值与 JavaBean 中对应的属性进行绑定。通常,我们会把一些配置信息(例如,数据库配置)放在配置文件中,然后通过 Java 代码去读取该配置文件,并且把配置文件中指定的配置封装到 JavaBean(实体类)中。
SpringBoot 提供了以下 2 种方式进行配置绑定:
使用 @ConfigurationProperties 注解
使用 @Value 注解
1. @ConfigurationProperties
通过 Spring Boot 提供的 @ConfigurationProperties 注解,可以将全局配置文件中的配置数据绑定到 JavaBean 中。
在 “ Springboot基础知识(01)- Spring Boot 简介、配置 Spring Boot 和 Spring Initializr ” 里 SpringbootBasic 项目基础上,修改如下。
1) 创建 src/main/resources/application.yml 文件(中间目录不存在的,新建目录,下同)
1 person:
2 firstName: Tester
3 age: 20
4 male: false
5 birth: 2000/01/01
6 maps: { key1: value1, key2: value2 }
7 lists:
8 - spring
9 - springboot
10 dog:
11 name: dd
12 age: 5
如果转换成 application.properties,对应的格式如下:
person.firstName=Tester
person.age=20
person.male=false
person.birth=2000/01/01
person.maps.key1=value1
person.maps.key2=value2
person.lists[0]=spring
person.lists[1]=springboot
person.dog.name=dd
person.dog.age=5
2) 创建 src/main/java/com/example/entity/Person.java 文件,并将配置文件中的属性映射到这个实体类上,代码如下。
1 package com.example.entity;
2
3 import java.util.Date;
4 import java.util.List;
5 import java.util.Map;
6 import org.springframework.stereotype.Component;
7 import org.springframework.boot.context.properties.ConfigurationProperties;
8
9 /**
10 * 将配置文件中配置的每一个属性的值,映射到这个组件中
11 * @ConfigurationProperties:告诉 SpringBoot 将本类中的所有属性和配置文件中相关的配置进行绑定;
12 * prefix = "person":配置文件中哪个下面的所有属性进行一一映射
13 */
14 @Component
15 @ConfigurationProperties(prefix = "person")
16 public class Person {
17 private String firstName;
18 private Integer age;
19 private Boolean male;
20 private Date birth;
21 private Map<String, Object> maps;
22 private List<Object> lists;
23 private Dog dog;
24 public Person() {
25 }
26 public Person(String firstName, Integer age, Boolean male, Date birth,
27 Map<String, Object> maps, List<Object> lists, Dog dog) {
28 this.firstName = firstName;
29 this.age = age;
30 this.male = male;
31 this.birth = birth;
32 this.maps = maps;
33 this.lists = lists;
34 this.dog = dog;
35 }
36 public String getFirstName() {
37 return firstName;
38 }
39 public void setFirstName(String firstName) {
40 this.firstName = firstName;
41 }
42 public Integer getAge() {
43 return age;
44 }
45 public void setAge(Integer age) {
46 this.age = age;
47 }
48 public Boolean getMale() {
49 return male;
50 }
51 public void setMale(Boolean male) {
52 this.male = male;
53 }
54 public Date getBirth() {
55 return birth;
56 }
57 public void setBirth(Date birth) {
58 this.birth = birth;
59 }
60 public Map<String, Object> getMaps() {
61 return maps;
62 }
63 public void setMaps(Map<String, Object> maps) {
64 this.maps = maps;
65 }
66 public List<Object> getLists() {
67 return lists;
68 }
69 public void setLists(List<Object> lists) {
70 this.lists = lists;
71 }
72 public Dog getDog() {
73 return dog;
74 }
75 public void setDog(Dog dog) {
76 this.dog = dog;
77 }
78 @Override
79 public String toString() {
80 return "Person {" +
81 "firstName = '" + firstName + '\'' +
82 ", age = " + age +
83 ", male = " + male +
84 ", birth = " + birth +
85 ", maps = " + maps +
86 ", lists = " + lists +
87 ", dog = " + dog +
88 '}';
89 }
90 }
注:
只有在容器中的组件,才会拥有 SpringBoot 提供的强大功能。如果我们想要使用 @ConfigurationProperties 注解进行配置绑定,那么首先就要保证该对 JavaBean 对象在 IoC 容器中,所以需要用到 @Component 注解来添加组件到容器中。
JavaBean 上使用了注解 @ConfigurationProperties(prefix = "person") ,它表示将这个 JavaBean 中的所有属性与配置文件中以“person”为前缀的配置进行绑定。
3) 创建 src/main/java/com/example/entity/Dog.java 文件,代码如下。
1 package com.example.entity;
2
3 public class Dog {
4 private String name;
5 private String age;
6 public Dog() {
7 }
8 public Dog(String name, String age) {
9 this.name = name;
10 this.age = age;
11 }
12 public void setName(String name) {
13 this.name = name;
14 }
15 public void setAge(String age) {
16 this.age = age;
17 }
18 public String getName() {
19 return name;
20 }
21 public String getAge() {
22 return age;
23 }
24
25 @Override
26 public String toString() {
27 return "Dog {" +
28 "name = " + name +
29 ", age = " + age +
30 '}';
31 }
32 }
4) 创建 src/main/java/com/example/ApplicationContextUtils.java 文件
1 package com.example;
2
3 import org.springframework.beans.BeansException;
4 import org.springframework.context.ApplicationContext;
5 import org.springframework.context.ApplicationContextAware;
6 import org.springframework.stereotype.Component;
7
8 /*
9 * 在非 SpringBoot 工厂管理的普通类中,如果要获取工厂管理的对象,不能再使用 @Autowired 等注入的注解,
10 * SpringBoot 提供了一个 “ApplicationContextAware” 接口,实现此接口,即可获取工厂管理的全部内容。
11 */
12 @Component
13 public class ApplicationContextUtils implements ApplicationContextAware {
14 private static ApplicationContext applicationContext;
15
16 @Override
17 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
18 this.applicationContext = applicationContext;
19 }
20
21 // 通过属性名获取对象
22 public static Object getById(String id){
23 Object bean = applicationContext.getBean(id);
24 return bean;
25 }
26
27 // 通过属性类获取对象
28 public static Object getByClass(Class clazz){
29 Object bean = applicationContext.getBean(clazz);
30 return bean;
31 }
32 // 通过属性类获取对象
33 public static Object getByNameAndClass(String id,Class clazz){
34 Object bean = applicationContext.getBean(id,clazz);
35 return bean;
36 }
37
38 }
5) 修改 src/main/java/com/example/App.java 文件
1 package com.example;
2
3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6 import com.example.entity.Person;
7
8 @SpringBootApplication
9 public class App {
10
11 public static void main(String[] args) {
12 SpringApplication.run(App.class, args);
13
14 Person person = (Person) ApplicationContextUtils.getById("person");
15 System.out.println(person);
16 }
17
18 }
6) 运行
Person {name = 'Tester', age = 20, male = false, birth = Sat Jan 01 00:00:00 CST 2000, maps = {key1=value1, key2=value2}, lists = [spring, springboot], dog = Dog {name = dd, age = 5}}
2. @Value
当我们只需要读取配置文件中的某一个配置时,可以通过 @Value 注解获取。
在上文 SpringbootBasic 项目基础上,修改如下。
1) 创建 src/main/java/com/example/entity/Person2.java 文件,代码如下。
1 package com.example.entity;
2
3 import java.util.Date;
4 import java.util.List;
5 import java.util.Map;
6
7 import org.springframework.beans.factory.annotation.Value;
8 import org.springframework.stereotype.Component;
9
10 @Component("person2")
11 public class Person2 {
12 @Value("${person.firstName}")
13 private String firstName;
14 @Value("${person.age}")
15 private Integer age;
16 @Value("${person.male}")
17 private Boolean male;
18 @Value("${person.birth}")
19 private Date birth;
20 private Map<String, Object> maps;
21 private List<Object> lists;
22 private Dog dog;
23
24 public Person2() {
25 }
26 public Person2(String firstName, Integer age, Boolean male, Date birth,
27 Map<String, Object> maps, List<Object> lists, Dog dog) {
28 this.firstName = firstName;
29 this.age = age;
30 this.male = male;
31 this.birth = birth;
32 this.maps = maps;
33 this.lists = lists;
34 this.dog = dog;
35 }
36
37 // getter 和 setter 方法参考上文 Person 类,略
38
39 @Override
40 public String toString() {
41 return "Person2 {" +
42 "firstName = '" + firstName + '\'' +
43 ", age = " + age +
44 ", male = " + male +
45 ", birth = " + birth +
46 ", maps = " + maps +
47 ", lists = " + lists +
48 ", dog = " + dog +
49 '}';
50 }
51 }
2) 修改 src/main/java/com/example/App.java 文件
1 package com.example;
2
3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6 import com.example.entity.Person;
7 import com.example.entity.Person2;
8
9 @SpringBootApplication
10 public class App {
11
12 public static void main(String[] args) {
13 SpringApplication.run(App.class, args);
14
15 Person person = (Person) ApplicationContextUtils.getById("person");
16 System.out.println(person);
17
18 Person person2 = (Person2) ApplicationContextUtils.getById("person2");
19 System.out.println(person2);
20 }
21
22 }
3) 运行
...
Person2 {firstName = Tester, age = 20, male = false, birth = Sat Jan 01 00:00:00 CST 2000, maps = null, lists = null, dog = null}
3. @Value 与 @ConfigurationProperties 对比
@Value 和 @ConfigurationProperties 注解都能读取配置文件中的属性值并绑定到 JavaBean 中,但两者存在以下不同。
1) 使用位置不同
@ConfigurationProperties:标注在 JavaBean 的类名上;
@Value:标注在 JavaBean 的属性上。
2) 功能不同
@ConfigurationProperties:用于批量绑定配置文件中的配置;
@Value:只能一个一个的指定需要绑定的配置。
3) 松散绑定支持不同
@ConfigurationProperties:支持松散绑定(松散语法),例如实体类 Person 中有一个属性为 firstName,那么配置文件中的属性名支持以下写法:
person.firstName
person.first-name
person.first_name
PERSON_FIRST_NAME
@Vaule:不支持松散绑定。
4) SpEL 支持不同
@ConfigurationProperties:不支持 SpEL 表达式;
@Value:支持 SpEL 表达式。
5) 复杂类型封装
@ConfigurationProperties:支持所有类型数据的封装,例如 Map、List、Set、以及对象等;
@Value:只支持基本数据类型的封装,例如字符串、布尔值、整数等类型。
6) 应用场景不同
@Value 和 @ConfigurationProperties 两个注解之间,并没有明显的优劣之分,它们只是适合的应用场景不同而已。
若只是获取配置文件中的某项值,则推荐使用 @Value 注解;
若专门编写了一个 JavaBean 来和配置文件进行映射,则建议使用 @ConfigurationProperties 注解。
在选用时,根据实际应用场景选择合适的注解能达到事半功倍的效果。
4. @PropertySource
如果将所有的配置都集中到 application.properties 或 application.yml 中,那么这个配置文件会十分的臃肿且难以维护,因此我们通常会将与 Spring Boot 无关的配置(例如自定义配置)提取出来,写在一个单独的配置文件中,并在对应的 JavaBean 上使用 @PropertySource 注解指向该配置文件。
@PropertySource 指定加载哪个文件,@ConfigurationProperties 指定加载文件中的哪一类属性。@PropertySource + @ConfigurationProperties 解决 @ConfigurationProperties 只能加载主文件内属性问题。
1) 绑定自定义 *.properties
在上文 SpringbootBasic 项目基础上,修改如下。
(1) 创建 src/main/resources/person3.properties 文件,代码如下。
person3.first-name=Tester3
person3.age=23
person3.birth=2005/05/05
person3.male=false
person3.maps.key1=value3
person3.maps.key2=value4
person3.lists=e,f,g
person3.dog.name=dd3
person3.dog.age=8
(2) 创建 src/main/java/com/example/entity/Person3.java 文件,代码如下。
1 package com.example.entity;
2
3 import java.util.Date;
4 import java.util.List;
5 import java.util.Map;
6
7 import org.springframework.boot.context.properties.ConfigurationProperties;
8 import org.springframework.context.annotation.PropertySource;
9 import org.springframework.stereotype.Component;
10
11 @Component("person3")
12 @ConfigurationProperties(prefix = "person3")
13 @PropertySource(value = "classpath:person3.properties")
14 public class Person3 {
15 private String firstName;
16 private Integer age;
17 private Boolean male;
18 private Date birth;
19 private Map<String, Object> maps;
20 private List<Object> lists;
21 private Dog dog;
22 public Person3() {
23 }
24 public Person3(String firstName, Integer age, Boolean male, Date birth,
25 Map<String, Object> maps, List<Object> lists, Dog dog) {
26 this.firstName = firstName;
27 this.age = age;
28 this.male = male;
29 this.birth = birth;
30 this.maps = maps;
31 this.lists = lists;
32 this.dog = dog;
33 }
34
35 // getter 和 setter 方法参考上文 Person 类,略
36
37 @Override
38 public String toString() {
39 return "Person3 {" +
40 "firstName = " + firstName +
41 ", age = " + age +
42 ", male = " + male +
43 ", birth = " + birth +
44 ", maps = " + maps +
45 ", lists = " + lists +
46 ", dog = " + dog +
47 '}';
48 }
49 }
(3) 修改 src/main/java/com/example/App.java 文件
1 package com.example;
2
3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6 import com.example.entity.Person;
7 import com.example.entity.Person2;
8 import com.example.entity.Person3;
9
10 @SpringBootApplication
11 public class App {
12
13 public static void main(String[] args) {
14 SpringApplication.run(App.class, args);
15
16 Person person = (Person) ApplicationContextUtils.getById("person");
17 System.out.println(person);
18
19 Person person2 = (Person2) ApplicationContextUtils.getById("person2");
20 System.out.println(person2);
21
22 Person person3 = (Person3) ApplicationContextUtils.getById("person3");
23 System.out.println(person3);
24 }
25
26 }
(4) 运行
Person3 {firstName = Tester3, age = 23, male = false, birth = Thu May 05 00:00:00 CST 2005, maps = {key2=value4, key1=value3}, lists = [e, f, g], dog = Dog {name = dd3, age = 8}}
2) 绑定自定义 *.yml
@PropertySource 默认不支持 yml 文件的解析,需要重写 DefaultPropertySourceFactory 的 createPropertySource 方法。
在上文 SpringbootBasic 项目基础上,修改如下。
(1) 创建 src/main/resources/person4.yml 文件,代码如下。
1 person4:
2 firstName: Tester4
3 age: 26
4 male: true
5 birth: 2008/08/08
6 maps: { key1: value5, key2: value6 }
7 lists:
8 - spring
9 - springmvc
10 - spirngboot
11 dog:
12 name: dd4
13 age: 12
(2) 创建 src/main/com/example/CustomPropertySourceFactory.java 文件,代码如下。
1 package com.example;
2
3 import java.io.IOException;
4 import java.util.List;
5
6 import org.springframework.core.io.support.DefaultPropertySourceFactory;
7 import org.springframework.core.io.support.EncodedResource;
8 import org.springframework.core.env.PropertySource;
9 import org.springframework.boot.env.YamlPropertySourceLoader;
10
11 public class CustomPropertySourceFactory extends DefaultPropertySourceFactory {
12
13 @Override
14 public PropertySource<?> createPropertySource(String name, EncodedResource resource) throws IOException {
15 if (resource == null){
16 return super.createPropertySource(name, resource);
17 }
18 List<PropertySource<?>> sources = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
19 return sources.get(0);
20 }
21 }
(3) 创建 src/main/java/com/example/entity/Person4.java 文件,代码如下。
1 package com.example.entity;
2
3 import java.util.Date;
4 import java.util.List;
5 import java.util.Map;
6
7 import org.springframework.boot.context.properties.ConfigurationProperties;
8 import org.springframework.context.annotation.PropertySource;
9 import org.springframework.stereotype.Component;
10
11 import com.example.CustomPropertySourceFactory;
12
13 @Component("person4")
14 @ConfigurationProperties(prefix = "person4")
15 @PropertySource(value = "classpath:person4.yml", factory = CustomPropertySourceFactory.class)
16 public class Person4 {
17 private String firstName;
18 private Integer age;
19 private Boolean male;
20 private Date birth;
21 private Map<String, Object> maps;
22 private List<Object> lists;
23 private Dog dog;
24 public Person4() {
25 }
26 public Person4(String firstName, Integer age, Boolean male, Date birth,
27 Map<String, Object> maps, List<Object> lists, Dog dog) {
28 this.firstName = firstName;
29 this.age = age;
30 this.male = male;
31 this.birth = birth;
32 this.maps = maps;
33 this.lists = lists;
34 this.dog = dog;
35 }
36
37 // getter 和 setter 方法参考上文 Person 类,略
38
39 @Override
40 public String toString() {
41 return "Person4 {" +
42 "firstName = " + firstName +
43 ", age = " + age +
44 ", male = " + male +
45 ", birth = " + birth +
46 ", maps = " + maps +
47 ", lists = " + lists +
48 ", dog = " + dog +
49 '}';
50 }
51 }
(4) 修改 src/main/java/com/example/App.java 文件
1 package com.example;
2
3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication;
5
6 import com.example.entity.Person;
7 import com.example.entity.Person2;
8 import com.example.entity.Person3;
9 import com.example.entity.Person4;
10
11 @SpringBootApplication
12 public class App {
13
14 public static void main(String[] args) {
15 SpringApplication.run(App.class, args);
16
17 Person person = (Person) ApplicationContextUtils.getById("person");
18 System.out.println(person);
19
20 Person person2 = (Person2) ApplicationContextUtils.getById("person2");
21 System.out.println(person2);
22
23 Person person3 = (Person3) ApplicationContextUtils.getById("person3");
24 System.out.println(person3);
25
26 Person person4 = (Person4) ApplicationContextUtils.getById("person4");
27 System.out.println(person4);
28 }
29
30 }
(5) 运行
Person4 {firstName = Tester4, age = 26, male = true, birth = Fri Aug 08 00:00:00 CST 2008, maps = {key1=value5, key2=value6}, lists = [spring, springmvc, spirngboot], dog = Dog {name = dd4, age = 12}}