Spring Boot 的配置文件和自动配置原理

  1. 使用 Spring Initializr 快速创建 Spring Boot 项目

    • IDEA:使用 Spring Initializr 快速创建项目

      ​ 继承关系-springboot 的 maven 项目

      • 使用 spring initializr 新建一个父 maven,type 选择 POM
      • 在父 maven 项目中新建模块,使用 spring initializr 新建一个子 maven,type选择 maven project
      • 修改子项目中的继承方式:

        之前:

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.6.6</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>

        修改后:

        <parent>
            <groupId>com.SpringBoot</groupId>
            <artifactId>springboot_parent</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </parent>

        spring_initializr 继承 springboot_parent 继承 spring-boot-starter-parent

    • 默认生成的 Spring Boot 项目:

      resource文件夹中的目录结构

      • static:保存所有的静态资源,js css images;
      • template:保存所有的模板页面;(Spring Boot 默认 jar 包使用嵌入式的 Tomcat,默认不支持 JSP 页面);可以使用模板引擎(freemarker、thymeleaf)
      • application.properties:Spring Boot 应用的配置文件;可以修改一下默认设置;
  2. 自定义 SpringApplication

    其他的上官网看。

    • lazy-initialization:懒加载

      # 懒加载设置,要在使用 bean 的时候才会去创建 bean 实例,否则不创建
      spring.main.lazy-initialization=true
  3. 配置文件的使用

    SpringBoot 使用一个全局的配置文件或者叫核心配置文件,配置文件名在约定的情况下 名字是固定的

    配置文件的作用:修改 SpringBoot 自动配置的默认值;SpringBoot 在底层都给我们自动配置好;

    两种配置文件的格式:

    1. application.properties 的用法:扁平的 k/v 格式。

      server.port=8081
      server.servlet.context-path=/wyu
    2. application.yml 的用法:树型结构。

      server:
          port: 8081
          servlet:
              context-path: /wyu

      建议使用 yml,因为它的可读性更强。

  • YAML语法:

    k:(空格)v:表示一对键值对(空格必须有);

    以空格的缩进来空值层级关系;只要是左对齐的一列数据,都是同一个层级的;

    属性和值也是大小写敏感;

  • 配置文件加载顺序:

    • application.yml
    • application.yaml
    • application.properties
  • 外部约定配置文件加载顺序:

    springboot 启动还会扫描以下位置的 application.properties 或者 application.yml 文件作为 Spring Boot 的默认配置文件

    下面无序列表优先级从低到高

    • classpath 根目录下的

      image-20220412141330192

    • classpath 根 config/

      image-20220412141257617

    • 项目根目录

      如果当前项目是继承/耦合关系 maven 项目的话,项目根目录为父 maven 项目的根目录。

      image-20220412141847161

    • 项目根目录 /config

      image-20220412142104682

    • 直接子目录 /config

      image-20220412143826880

  • 配置文件值注入

    image-20220425024303286

    可以使用 @Value 标签一个一个属性绑定,也可以使用 @ConfigurationProperties 标签根据 yml 或者 properties 文件一次性绑定

    • 松散绑定

      user:
        user-name: 啊章
      user:
        userName: 啊章
      user:
        user_name: 啊章
      user:
        USERNAME: 啊章

      以上四种命名是可以自动绑定 bean 属性 User.username

    • 如果输出中文乱码,则需要调一下setting:

    image-20220425024608880

    • @Value@ConfigurationProperties 的获取值比较:
    @ConfigurationProperties@Value
    功能批量注入配置文件中的属性一个个指定
    松散绑定支持支持,有限
    SpEL不支持支持
    JSR303数据校验支持不支持
    复杂类型封装支持不支持
    自动提示支持不支持
    • 导入配置文件处理器并且勾选如图设置,以后编写配置就有提示了:
    <!-- 会生成 META-INF 元数据  用于提供 idea 自动提示配置文件的 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <!-- option: 如果依赖不会传播  就是说别的项目依赖本项目,那么本项目的这个依赖不会被别的项目依赖 -->
        <optional>true</optional>
    </dependency>

    image-20220425025520512

    • 配置文件赋值:

      • 对象、Map(属性和值)(键值对):

        • 对象是 k : v 的方式

            girl-friend:
              18 : IU
              20 : 庄达菲
        • 行内写法:

            girl-friend: {18: IU,20: 庄达菲}
      • 数组(List、Set):

        • 用 - 值 表示数组中的一个元素

            hobbies:
              - 唱
              - 跳
              - rap
              - 篮球
        • 行内写法

            hobbies: [唱,跳,rap,篮球]
      • JSR303数据校验

        image-20220425033655210

        <!-- 数据校验 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        @NotNull 要使用 javax.validation 包下面的验证注解

  • 配置文件占位符

    • 随机数

      #随机值
      ${random.value}
      #随机 int 值
      ${random.int}
      #随机值
      ${random.long}
      #随机值
      ${random.uuid}
      # 10 以内的随机 int 值
      ${random.int(10)}
      #范围为 1024 到 65536 的随机 int 值
      ${random.}int[1024,65536]}
    • 占位符获取之前配置的值,如果没有可以使用:指定默认值

  • Profile文件的加载

    对于应用程序来说,不同的环境需要不同的配置。

    1. 多 Profile 文件

      • Spring 官方给出的语法规则是 application-{profile}.properties(.yaml/.yml)
      • 如果需要创建自定义的 properties 文件时,可以用 application-xxx.properties 的命名方式。

        • 开发环境下:

          image-20220412154909418

        • 生产环境下:

          image-20220412154920749

      • 若我们需要在两种环境下进行切换,只需要在 application.properties 中加入如下内容即可。

        spring.profiles.active=prod
    2. 激活指定 profile

      • 在配置文件中指定 spring.profiles.active=dev
      • 命令行:

        java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;
        • 还可以通过 spring.config.location 来改变默认的配置文件

          使用 spring.config.location 指定的配置文件,是不会进行互补。

          java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.location=D:/application.properties
        • 还可以通过 spring.config.name 来改变默认的配置文件

          java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.name=application-prod;
    3. 所有配置文件按以下顺序考虑:优先级从低到高

      1. 打包在 jar 中配置文件
      2. 打包在 jar 中 profile
      3. 打包在 jar 之外的配置文件
      4. 打包在 jar 之外的 profile
    4. 通配符位置

      如果具有某些 Redis 配置和某些 mysql 配置,则可能需要将这两个配置分开,同时要求这两个配置都存在于 application.properties 文件中。这可能会导致两个不同的 application.properties 文件安装在诸如不同的位置 /config/redis/application.properties 和 /config/mysql/application.properties。在这种情况下,通配符位置为 config/*/,将导致两个文件都被处理。

    5. 配置文件读取方式:优先级从低到高

      • @PropertySource@Configuration 类上的注解。请注意,Environment 在刷新应用程序上下文之前,不会将此类属性源添加到中。现在配置某些属性(如 logging. 和 spring.main. 在刷新开始之前先读取)为时已晚。

        • 会和约定的配置文件形成互补
        • 一定要指定 .properties 配置
        @SpringBootApplication
        @PropertySource("classpath:appSource.properties")
        public class Application {
        
            public static void main(String[] args) throws IOException {}
      

    - 默认属性(通过设置指定 SpringApplication.setDefaultProperties)

      - 会和约定的配置文件形成互补

      ```java
      @SpringBootApplication
      public class Application {
      
          public static void main(String[] args) throws IOException {
              SpringApplication springApplication = new SpringApplication(Application.class);
      
              // 创建 properties
              Properties properties = new Properties();
              // 通过当前类的 ClassLoader
              InputStream is = Application.class.getClassLoader().getResourceAsStream("app.properties");
              // 将输入流读取成 properties 对象
              properties.load(is);
      
              springApplication.setDefaultProperties(properties);
              springApplication.run(args);
          }
      
      }
      ```

      

    - 配置数据(例如 application.properties 文件)

      - 约定配置文件

    - 操作系统环境变量。

      - 会使约定配置文件失效

      - idea 

        ![image-20220422040551533](https://static-upyun.hackerjk.top/markdown/image-20220422040551533.png!)

      - windows

        ![image-20220422040837561](https://static-upyun.hackerjk.top/markdown/image-20220422040837561.png!)

    - Java 系统属性(System.getProperties())。

      - 会使约定配置文件失效

      - idea

        ![image-20220422041130067](https://static-upyun.hackerjk.top/markdown/image-20220422041130067.png!)

      - 命令行 java 属性

        ![image-20220422041323887](https://static-upyun.hackerjk.top/markdown/image-20220422041323887.png!)

    - JNDI 属性 java:comp/env。

    - ServletContext 初始化参数。

      ![image-20220422041429270](https://static-upyun.hackerjk.top/markdown/image-20220422041429270.png!)

    - ServletConfig 初始化参数。

      ![image-20220422041509283](https://static-upyun.hackerjk.top/markdown/image-20220422041509283.png!)

    - 来自的属性 SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的嵌入式 JSON)。

    - 命令行参数。

      - 会使约定配置文件失效

      ```cmd
      java -jar configuration_file-0.0.1-SNAPSHOT.jar --spring.profiles.location=D:/application.properties
      ```

      

    - properties 测试中的属性。可用于测试应用程序的特定部分 @SpringBootTest 的测试注释和注释。

    - @TestPropertySource 测试中的注释。

      - 用在单元测试上的

        ![image-20220422043022209](https://static-upyun.hackerjk.top/markdown/image-20220422043022209.png!)

    - $HOME/.config/spring-boot 当 devtools 处于活动状态时,目录中的 Devtools全局设置属性。
  • 配置文件加载位置
  1. Spring Boot 的配置和自动配置原理

    • @SpringBootApplication:Spring Boot 应用标注在某个类上说明这个类是 SpringBoot 的主配置类,SpringBoot 需要运行这个类的 main 方法来启动 SpringBoot 应用;

      image-20220427150357061

      @Target(ElementType.TYPE)    //设置当前注解可以标记在哪
      @Retention(RetentionPolicy.RUNTIME)    //当注解标注的类编译以什么方式保留
          RetentionPolicy.RUNTIME 会被 jvm 加载 | 反射的方式:class.getAnnotionbytype(Autowreid.class)
      @Documented        //java doc 会生成注解信息
      @Inherited        //是否会被继承
      
      @SpringBootConfiguration    //Spring Boot 的配置类;标注在某个类上,表示这是一个 Spring Boot 的配置类;
      
      @Configuration    //配置类上来标注这个注解;配置类——配置文件;配置类也是同期中的一个组件;@Component
      
      @EnableAutoConfiguration    //开启自动配置功能;以前我们需要配置的东西,Spring Boot 帮我们自动配置;@EnableAutoConfiguration 告诉 SpringBoot 开启自动配置功能;这样自动配置才能生效;
      
      @ComponentScan    //扫描包 相当于在 spring.xml 配置中 <context:component-scan>    但是没有指定 basepackage,如果没有指定,spring 底层会自动扫描当前配置类所有在的包
      
      TypeExcludeFilter:springboot    //对外提供的扩展类,可以供我们去按照我们的方式进行排除
      
      AutoConfigurationExcludeFilter    //排除所有配置类并且是自动配置类中里面的其中一个
      
      @AutoConfigurationPackage    //将当前配置类所在包保存在 BasePackages 的 Bean 中。供 spring 内部使用
    • 以 HttpEncodingAutoConfiguration (Http 编码自动配置)为例解释自动配置原理;

      @Configuration(proxyBeanMethods = false)
      @EnableConfigurationProperties(ServerProperties.class)
      @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
      @ConditionalOnClass(CharacterEncodingFilter.class)
      @ConditionalOnProperty(prefix = "server.servlet.encoding", value = "enabled", matchIfMissing = true)
      public class HttpEncodingAutoConfiguration {
      
          private final Encoding properties;
      
          public HttpEncodingAutoConfiguration(ServerProperties properties) {
              this.properties = properties.getServlet().getEncoding();
          }
      
          @Bean
          @ConditionalOnMissingBean
          public CharacterEncodingFilter characterEncodingFilter() {
              CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
              filter.setEncoding(this.properties.getCharset().name());
              filter.setForceRequestEncoding(this.properties.shouldForce(Encoding.Type.REQUEST));
              filter.setForceResponseEncoding(this.properties.shouldForce(Encoding.Type.RESPONSE));
              return filter;
          }
      • @Configuration(proxyBeanMethods = false)

        标记了 @Configuration,Spring 底层会给配置创建 cglib 动态代理。

        作用:防止每次调用本类的 Bean 方法而重新创建对象,Bean 是默认单例的。

      • @EnableConfigurationProperties(ServerProperties.class)

        启用可以再配置类设置的属性 对应的类

      • @xxxConditional 根据当前不同的条件判断,决定这个配置类是否生效?
      • @Conditional 派生注解(Spring 注解版原生的 @Conditional 作用)

        作用:必须是 @Conditional 指定的条件成立,才给容器中添加组件,配置配里面的所有内容才能生效;

        @Conditional 扩展注解作用(判断是否满足当前指定条件)
        @ConditionalOnJava系统的 java 版本是否符合要求
        @ConditionalOnBean容器中存在指定的 Bean;
        @ConditionalOnMissingBean容器中不存在指定的 Bean;
        @ConditionalOnExpression满足 SpEL 表达式指定
        @ConditionalOnClass系统中有指定的类
        @ConditionalOnOnMissingClass系统中没有指定的类
        @ConditionalOnSingleCandidate容器中只有一个指定的 Bean,或者这个 Bean 是首选 Bean
        @ConditionalOnProperty系统中指定的属性是否有指定的值
        @ConditionalOnResource类路径下是否存在指定资源文件
        @ConditionalOnWebApplication当前是 Web 环境
        @ConditionalOnNotWebApplication当前不是 Web 环境
        @ConditionalOnJndiJNDI 存在指定项
      • 可以通过设置配置文件中:启用 debug=true 属性来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效

        ============================
        CONDITIONS EVALUATION REPORT
        ============================
        
        
        Positive matches:    **表示自动配置类启用的**
        -----------------
        ......
        
        
        Negative matches:    **没有匹配成功的自动配置类**
        -----------------
        ......