配置绑定就是把配置文件中的值与 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}}