Spring基础知识(23)- Spring Boot (四)


导入 Spring 配置、默认配置文件、外部配置文件


1. 导入 Spring 配置


    默认情况下,Spring Boot 中是不包含任何的 Spring 配置文件的,即使我们手动添加 Spring 配置文件到项目中,也不会被识别。那么 Spring Boot 项目如何导入 Spring 配置?

    Spring Boot 为了我们提供了以下 2 种方式来导入 Spring 配置:

        使用 @ImportResource 注解加载 Spring 配置文件
        使用全注解方式加载 Spring 配置

    1) @ImportResource 导入 Spring 配置文件

        在主启动类上使用 @ImportResource 注解可以导入一个或多个 Spring 配置文件,并使其中的内容生效。

        示例,在 “” 里 SpringbootBasic 项目基础上,修改如下。

        (1) 创建 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             public class Person {
10                 private String firstName;
11                 private Integer age;
12                 private Boolean male;
13                 private Date birth;
14                 private Map maps;
15                 private List lists;
16                 private Dog dog;
17                 public Person() {
18                 }
19                 public Person(String firstName, Integer age, Boolean male, Date birth,
20                             Map maps, List lists, Dog dog) {
21                     this.firstName = firstName;
22                     this.age = age;
23                     this.male = male;
24                     this.birth = birth;
25                     this.maps = maps;
26                     this.lists = lists;
27                     this.dog = dog;
28                 }
29     
30                 // 省略 getter 和 setter 方法
31 
32                 @Override
33                 public String toString() {
34                     return "Person {" +
35                             "firstName = '" + firstName + '\'' +
36                             ", age = " + age +
37                             ", male = " + male +
38                             ", birth = " + birth +
39                             ", maps = " + maps +
40                             ", lists = " + lists +
41                             ", dog = " + dog +
42                             '}';
43                 }
44             }


        (2) 创建 src/main/java/com/example/service/PersonService.java 文件

1             package com.example.service;
2 
3             import com.example.entity.Person;
4 
5             public interface PersonService {
6                 public Person getPersonInfo();
7             }

 
        (3) 创建 src/main/java/com/example/service/PersonServiceImpl.java 文件

 1             package com.example.service;
 2 
 3             import org.springframework.beans.factory.annotation.Autowired;
 4             import com.example.entity.Person;
 5 
 6             public class PersonServiceImpl implements PersonService {
 7                 @Autowired
 8                 private Person person;
 9                 @Override
10                 public Person getPersonInfo() {
11                     return person;
12                 }
13             }


        (4) 创建 src/main/resources/spring-beans.xml 配置文件

1             <?xml version="1.0" encoding="UTF-8"?>
2             "http://www.springframework.org/schema/beans"
3                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4                 xsi:schemaLocation="http://www.springframework.org/schema/beans
5                                     http://www.springframework.org/schema/beans/spring-beans.xsd">
6 
7                 "personService" class="com.example.service.PersonServiceImpl">
8             


        (5) 创建 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 Boolean containsBean(String id){
23                     return applicationContext.containsBean(id);
24                 }
25 
26                 // 通过属性名获取对象
27                 public static Object getbyId(String id){
28                     Object bean = applicationContext.getBean(id);
29                     return bean;
30                 }
31 
32                 ...
33             }


        (6) 修改 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             @SpringBootApplication
 7             public class App {
 8 
 9                 public static void main(String[] args) {
10                     SpringApplication.run(App.class, args);
11 
12                     Boolean b = ApplicationContextUtils.containsBean("personService");
13                     if (b) {
14                         System.out.println("personService in IOC");
15                     } else {
16                         System.out.println("personService not in IOC");
17                     }
18                 }
19 
20             }


            运行:
        
                personService not in IOC

        (7) 在 App 类上使用 @ImportResource 注解,将 Spring 配置文件 beans.xml 加载到项目中,代码如下。

 1             ...
 2 
 3             import org.springframework.context.annotation.ImportResource;
 4 
 5             // 将 beans.xml 加载到项目中
 6             @ImportResource(locations = {"classpath:/spring-beans.xml"})
 7             @SpringBootApplication
 8             public class App {
 9                 public static void main(String[] args) {
10                    ...
11                 }
12             }


            再运行:

                personService in IOC


    2) 全注解方式加载 Spring 配置

        Spring Boot 推荐使用全注解的方式加载 Spring 配置,其实现方式如下:

            (1) 使用 @Configuration 注解定义配置类,替换 Spring 的配置文件;
            (2) 配置类内部定义一个或多个被 @Bean 注解的方法;
            (3) AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类扫描和构建 bean 定义(相当于 Spring 配置文件中的标签);
            (4) @Bean 注解方法的返回值以组件的形式添加到容器中,组件的 id 就是方法名。

        示例,在上文 SpringbootBasic 项目基础上,修改如下。

        (1) 创建 src/main/java/com/example/AppConfig.java 文件

 1             package com.example;
 2 
 3             import org.springframework.context.annotation.Bean;
 4             import org.springframework.context.annotation.Configuration;
 5 
 6             import com.example.service.PersonService;
 7             import com.example.service.PersonServiceImpl;
 8 
 9             @Configuration
10             public class AppConfig {
11 
12                 /**
13                 * 相当于 
14                 */
15                 @Bean
16                 public PersonService personService() {
17                     return new PersonServiceImpl();
18                 }
19 
20             }  


        (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             @SpringBootApplication
 7             public class App {
 8 
 9                 public static void main(String[] args) {
10                     SpringApplication.run(App.class, args);
11 
12                     Boolean b = ApplicationContextUtils.containsBean("personService");
13                     if (b) {
14                         System.out.println("personService in IOC");
15                     } else {
16                         System.out.println("personService not in IOC");
17                     }
18                 }
19 
20             }


            运行:
        
                personService in IOC


2. 默认配置文件

    通常情况下,Spring Boot 在启动时会将 resources 目录下的 application.properties 或 apllication.yml 作为其默认配置文件,我们可以在该配置文件中对项目进行配置。

    Spring Boot 项目中可以存在多个 application.properties 或 apllication.yml。启动时会扫描以下 5 个位置的  application.properties 或 apllication.yml 文件,并将它们作为 Spring boot 的默认配置文件。

        (1) file:./config/
        (2) file:./config/*/
        (3) file:./
        (4) classpath:/config/
        (5) classpath:/

        注:file: 指当前项目根目录;classpath: 指当前项目的类路径,即 resources 目录。

    以上所有位置的配置文件都会被加载,且它们优先级依次降低,序号越小优先级越高。其次,位于相同位置的 application.properties 的优先级高于 application.yml。

    所有位置的文件都会被加载,高优先级配置会覆盖低优先级配置,形成互补配置,即:

        (1) 存在相同的配置内容时,高优先级的内容会覆盖低优先级的内容;
        (2) 存在不同的配置内容时,高优先级和低优先级的配置内容取并集;
    
    示例,在 “” 里 SpringbootBasic 项目基础上,修改如下。

    1) 创建 src/main/java/com/example/entity/User.java 文件

 1         package com.example.entity;
 2 
 3         import org.springframework.stereotype.Component;
 4         import org.springframework.boot.context.properties.ConfigurationProperties;
 5 
 6         @Component
 7         @ConfigurationProperties(prefix = "user")
 8         public class User {
 9             private String username;
10             private Integer age;
11 
12             public User() {
13 
14             }
15 
16             public String getUsername() {
17                 return username;
18             }
19             public void setUsername(String username) {
20                 this.username = username;
21             }
22 
23             public Integer getAge() {
24                 return age;
25             }
26             public void setAge(Integer age) {
27                 this.age = age;
28             }
29             @Override
30             public String toString() {
31                 return "User {" +
32                         "username = " + username +
33                         ", age = " + age +
34                         '}';
35             }            
36         }


    2) 在项目根目录下创建 /application.yml 文件,配置如下。

        user:
            username: admin
            age: 18

    3) 在项目类路径下(src/main/resources)创建 application.yml 文件,配置如下。

        user:
            username: admin2
            age: 21

    4) 在项目类路径下的 config 目录下 (src/main/resources/config) 创建 application.yml 文件,配置如下。

        user:
            username: admin3
            age: 28

    5) 创建 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             // 通过属性类获取对象
34             public static Object getByNameAndClass(String id,Class clazz){
35                 Object bean = applicationContext.getBean(id,clazz);
36                 return bean;
37             }
38 
39         }


    6) 修改 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.User;
 7 
 8         @SpringBootApplication
 9         public class App {
10 
11             public static void main(String[] args) {
12                 SpringApplication.run(App.class, args);
13 
14                 User user = (User) ApplicationContextUtils.getById("user");
15                 System.out.println(user);
16             }
17 
18         }


       运行:

          User {username = admin, age = 18}

    注:对应项目根目录下 application.yml 文件里的配置。

3. 外部配置文件

    除了默认配置文件,Spring Boot 还可以加载一些位于项目外部的配置文件。我们可以通过如下 2 个参数,指定外部配置文件的路径:

        spring.config.location
        spring.config.additional-location

    1) spring.config.location

        可以先将 Spring Boot 项目打包成 JAR 文件,然后在命令行启动命令中,使用命令行参数 --spring.config.location,指定外部配置文件的路径。

            java -jar {JAR}  --spring.config.location={外部配置文件全路径}

        需要注意的是,使用该参数指定配置文件后,会使项目默认配置文件(application.properties 或 application.yml )失效,Spring Boot 将只加载指定的外部配置文件。

        示例,在上文修改过的 SpringbootBasic 项目基础上,进行演示。

        (1) 在本地目录 D:\temp 下,创建一个配置文件 outer-application.yml,配置如下。

            user:
                username: admin-out
                age: 32
        
        (2) 打包 jar

            打包操作参考 “” 的 “2. 创建 Maven Quickstart 项目" 下的 “5)使用 spring-boot-maven-plugin 插件运行打包”。

        (3) 运行 jar

            点击 IDEA 底部 Terminal 标签页,执行如下命令。

                java -jar target/SpringbootBasic-1.0-SNAPSHOT.jar --spring.config.location=D:\temp\outer-application.yml

            Terminal 输出:
            
                User {username = admin-out, age = 32}

    2) spring.config.additional-location

        还可以在 Spring Boot 启动时,使用命令行参数 --spring.config.additional-location 来加载外部配置文件。

            java -jar {JAR}  --spring.config.additional-location={外部配置文件全路径}

        与 --spring.config.location 不同,--spring.config.additional-location 不会使项目默认的配置文件失效,使用该命令行参数添加的外部配置文件会与项目默认的配置文件共同生效,形成互补配置,且其优先级是最高的,比所有默认配置文件的优先级都高。

        示例,在上文SpringbootBasic 项目基础上,运行如下命令

            java -jar target/SpringbootBasic-1.0-SNAPSHOT.jar --spring.config.additional-location=D:\temp\outer-application.yml

        Terminal 输出:
            
            User {username = admin-out, age = 32}

    3) IDEA Run/Debug Configuration 使用外部配置文件

        (1) 虚拟机参数方式

            Edit Configurations

                Click "+" add new configuration -> Select "Application"

                    Name: SpringbootBasic
                    Main class: com.example.App
                    VM options: -Dspring.config.additional-location=D:\temp\outer-application.yml

                -> Apply / OK

            Click Run "SpringbootBasic"

              User {username = admin-out, age = 32}

       (2) 程序运行参数方式

            Edit Configurations

                Click "+" add new configuration -> Select "Application"

                    Name: SpringbootBasic
                    Main class: com.example.App
                    Program arguments: --spring.config.additional-location=D:\temp\outer-application.yml

                -> Apply / OK

            Click Run "SpringbootBasic"

              User {username = admin-out, age = 32}