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'

相关推荐
凤山老林6 小时前
Spring Boot 集成 iText 7 实现动态 PDF 生成与电子签章:合同、报表场景实战
spring boot·后端·pdf·itext7·电子签章
卓怡学长6 小时前
w204基于springbo旅行社签证代理服务系统
java·spring boot·spring·intellij-idea
qwfy6 小时前
我要用 AI-native 架构,从零重建一套制造业 ERP/MES
vue.js·spring boot·架构
vipxieliang6 小时前
ValidX 在微服务架构中的验证策略
java·spring boot
❀͜͡傀儡师6 小时前
20 年沉淀,CAS 8.0 重新定义企业级 SSO:适配 JDK 25 与 Spring Boot 4.1
java·开发语言·spring boot
牧瀬クリスだ7 小时前
Spring统一功能处理
java·spring boot·spring·状态模式
Dreams_l8 小时前
基于SpringBoot实现的抽奖系统(一)
java·spring boot·后端
余槐i9 小时前
rollbackFor 不设置,Checked 异常不回滚:Spring 默认规则与两行修复的取舍
java·spring boot·后端·spring·事务管理
EatFan9 小时前
JunoYi 框架实践:Spring Boot 项目为什么拆成 framework、module、server 三层?
java·spring boot·后端·framework·module·模块化·junoyi
谢亮_vipxieliang10 小时前
ValidX 在微服务架构中的验证策略
java·spring boot·spring·spring cloud·hibernate