Spring Boot配置文件与自动配置原理深度剖析

一、配置文件概述

1.1 为什么选择YAML?

YAML以数据为中心,比JSON、XML等更适合做配置文件。对比来看:

|------------|--------------------------------------------|-----|
| 格式 | 配置示例 | 可读性 |
| XML | <server><port>8080</port></server> | 较差 |
| Properties | server.port=8080 | 一般 |
| YAML | server: port: 8080 | 优秀 |

二、YAML语法详解

2.1 基本语法

k:(空格)v:表示一对键值对,空格必须有,以空格的缩进来控制层级关系,左对齐的数据属于同一层级

属性和值大小写敏感

复制代码
server:
  port: 8080
  servlet:
    context-path: /boot3

2.2 值的写法

字面量(数字、字符串、布尔)

复制代码
name: zhangsan
age: 18
boss: true

字符串引号规则:

双引号"":不会转义特殊字符。name: "zhangsan \n lisi" 输出为换行效果

单引号'':会转义特殊字符。name: 'zhangsan \n lisi' 输出为 zhangsan \n lisi

对象、Map(键值对):

复制代码
person:
  lastName: zhangsan
  age: 18
  boss: false

数组(List、Set):

复制代码
lists:
  - a
  - b
  - c

行内写法:

复制代码
lists: [a, b, c]

三、配置文件值注入

3.1 @ConfigurationProperties 批量注入

创建Person.java,使用@ConfigurationProperties将配置文件中的属性映射到JavaBean中:

复制代码
# application.yml
person:
  last-name: 张三
  age: 18
  boss: false
  birth: 2017/12/15
  maps: {k1: v1, k2: 14}
  lists: [a, b, c]
  dog:
    name: 旺财
    age: 2

@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;
    // 加上getter/setter
}

3.2 @Value 单个注入

如果只是在某个业务逻辑中获取配置文件中的某一项值,使用@Value更加轻量:

复制代码
@RestController
public class HelloController {
    @Value("${person.last-name}")
    private String name;
    
    @Value("${server.port}")
    private String port;
}

3.3 两种方式对比

|------------|--------------------------|--------|
| 特性 | @ConfigurationProperties | @Value |
| 功能 | 批量注入配置文件属性 | 单个指定注入 |
| 松散绑定 | 支持 | 不支持 |
| SpEL | 不支持 | 支持 |
| JSR303数据校验 | 支持 | 不支持 |
| 复杂类型封装 | 支持 | 不支持 |

使用建议:如果专门编写了一个JavaBean来和配置文件进行映射,使用@ConfigurationProperties;如果只是在业务逻辑中获取某项配置值,使用@Value。

3.4 数据校验

@ConfigurationProperties支持JSR303数据校验:

复制代码
@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
    @Email  // lastName必须是邮箱格式
    private String lastName;
    // ...
}

3.5 @PropertySource 加载指定配置文件

默认情况下,@ConfigurationProperties从全局配置文件(application.properties/yml)中获取值。使用@PropertySource可以加载指定的配置文件:

复制代码
@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    // ...
}

3.6 @ImportResource 与 @Bean

Spring Boot默认不识别我们自己编写的Spring配置文件(如applicationContext.xml)。使用@ImportResource可以让配置文件生效:

复制代码
@ImportResource(locations = {"classpath:spring.xml"})
@SpringBootApplication
public class HelloWorldMainApplication {
    // ...
}

但Spring Boot更推荐使用全注解方式:

复制代码
@Configuration  // 表明这是一个配置类,替代Spring配置文件
public class MyAppConfig {
    
    @Bean  // 将方法返回值添加到容器中,默认id为方法名
    public HelloService helloService() {
        return new HelloService();
    }
}

四、Profile多环境配置

4.1 多Profile文件

主配置文件命名为application-{profile}.properties/yml,例如:

application-dev.properties ------ 开发环境

application-prod.properties ------ 生产环境

application-test.properties ------ 测试环境

默认使用application.properties的配置。

4.2 YAML多文档块

YAML支持在一个文件中使用---分隔多个文档块:

复制代码
# 默认配置
server:
  port: 8080
---
# dev环境
spring:
  profiles: dev
server:
  port: 8081
---
# prod环境
spring:
  profiles: prod
server:
  port: 8082

4.3 激活指定Profile

在配置文件中指定:

复制代码
spring:
  profiles:
    active: dev

命令行参数:

复制代码
java -jar app.jar --spring.profiles.active=dev

虚拟机参数:

复制代码
java -Dspring.profiles.active=dev -jar app.jar

五、配置文件加载位置与顺序

5.1 默认加载位置

Spring Boot启动时会扫描以下4个位置的配置文件,优先级由高到低:

  1. file:./config/ (项目根目录下的config文件夹)

  2. file:./ (项目根目录)

  3. classpath:/config/ (类路径下的config文件夹)

  4. classpath:/ (类路径根目录)

高优先级的配置会覆盖低优先级的配置,且所有位置的配置会互补合并。

5.2 外部配置加载顺序

Spring Boot支持从多种来源加载配置,优先级从高到低如下:

命令行参数(如 --server.port=8087)

来自java:comp/env的JNDI属性

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

操作系统环境变量

RandomValuePropertySource配置的random.*属性值

由jar包外向jar包内寻找:

jar包外部的application-{profile}.properties/yml(带profile)

jar包内部的application-{profile}.properties/yml(带profile)

jar包外部的application.properties/yml(不带profile)

jar包内部的application.properties/yml(不带profile)

@Configuration注解类上的@PropertySource

SpringApplication.setDefaultProperties()指定的默认属性

参考资料:Spring Boot官方文档 External Configuration

六、自动配置原理

6.1 从"配置地狱"到"开箱即用"

Spring框架自2003年诞生以来,凭借IoC和AOP迅速成为Java企业级开发的事实标准,但随着生态越来越庞大,"配置地狱"问题日益严重。Spring Boot的自动配置机制正是为解决这一问题而生。

6.2 自动配置的核心流程

第一步:启动主配置类

Spring Boot启动时加载主配置类(@SpringBootApplication标注的类),开启自动配置功能@EnableAutoConfiguration-。

第二步:加载自动配置类

@EnableAutoConfiguration通过@Import(AutoConfigurationImportSelector.class)导入组件。AutoConfigurationImportSelector的selectImports()方法会调用SpringFactoriesLoader.loadFactoryNames()。

SpringFactoriesLoader扫描所有JAR包类路径下的META-INF/spring.factories文件,将其中配置的EnableAutoConfiguration对应的值全部加载到容器中。

在spring.factories中,配置了上百个自动配置类(xxxAutoConfiguration):

复制代码
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\

注意:Spring Boot 3.0之后,自动配置的注册方式从spring.factories迁移到了AutoConfiguration.imports文件,加载更加高效。

第三步:条件过滤

每个自动配置类上都标注了@Conditional系列注解,只有满足条件时配置类才会生效:

复制代码
@Configuration
@ConditionalOnWebApplication          // 必须是Web环境
@ConditionalOnClass(CharacterEncodingFilter.class)  // 类路径存在指定类
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
    // ...
}

第四步:配置属性绑定

自动配置类从对应的xxxProperties类中获取配置属性,而xxxProperties类通过@ConfigurationProperties与配置文件绑定:

复制代码
@ConfigurationProperties(prefix = "spring.http.encoding")
public class HttpEncodingProperties {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    // ...
}

6.3 @Conditional 系列注解

|---------------------------------|------------------|
| 注解 | 作用 |
| @ConditionalOnClass | 类路径存在指定类时生效 |
| @ConditionalOnMissingClass | 类路径不存在指定类时生效 |
| @ConditionalOnBean | 容器中存在指定Bean时生效 |
| @ConditionalOnMissingBean | 容器中不存在指定Bean时生效 |
| @ConditionalOnProperty | 系统中指定的属性有指定的值时生效 |
| @ConditionalOnWebApplication | 当前是Web环境时生效 |
| @ConditionalOnNotWebApplication | 当前不是Web环境时生效 |

6.4 查看自动配置报告

在配置文件中添加debug=true,控制台会打印自动配置报告,方便查看哪些自动配置类生效、哪些没有生效:

properties

复制代码
debug=true

Positive matches:(自动配置类启用的)

DispatcherServletAutoConfiguration matched:

@ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'

@ConditionalOnWebApplication (required) found StandardServletEnvironment

Negative matches:(没有启动的自动配置类)

ActiveMQAutoConfiguration:

Did not match:

@ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory'

相关推荐
豆角焖肉1 小时前
Spring Boot入门实战:从零搭建第一个应用
java·spring boot·后端·自动配置
BUG指挥官1 小时前
Redis已接入AI
数据库·人工智能·spring boot·redis·spring cloud
m0_5873830012 小时前
全民健身解决方案软件开发实战:从架构设计到落地指南
java·spring boot·spring·架构·需求分析
azhou的代码园16 小时前
基于RFM模型的中小型企业客户管理系统
java·人工智能·spring boot·毕业设计
行百里er16 小时前
BeanPostProcessor:包装 Feign Client
spring boot·后端·监控
花生了什么事o16 小时前
自定义Spring Boot Starter全流程
java·spring boot·后端
摇滚侠17 小时前
《SpringBoot 3:入门与应用实战》第 12 章 JDBC 与事务 使用 JdbcTemplate 阅读笔记 31
android·spring boot·笔记
torpidcat17 小时前
ruoyi-vue-pro 若依芋道 java springboot +mybatis 生日查询相关
java·vue.js·spring boot
xcl092519 小时前
智慧场馆解决方案系统源码:从架构设计到部署实战
java·spring boot