Spring Boot 详细学习指南(下篇):配置文件 + 高级特性 + 自动配置原理

一、配置文件:修改默认配置的核心

Spring Boot 采用 "约定优于配置" 的思想,大部分配置都有默认值。但实际开发中,我们需要根据需求修改默认配置(比如修改端口、配置数据源),这就需要用到配置文件。

1. 配置文件的格式和作用

Spring Boot 有两种全局配置文件,文件名是固定的:

  • application.properties:键值对格式,入门友好。
  • application.yml:YAML 格式,简洁高效,推荐使用。

作用:修改 Spring Boot 自动配置的默认值(比如端口、数据源、字符编码等)。

两种格式对比(修改端口为 8081)
  • application.properties

    server.port=8081

  • application.yml

java 复制代码
server:
  port: 8081

2. YAML 语法详解(重点)

YAML 是一种以数据为中心的配置语言,比 XML、properties 更简洁,语法规则如下:

(1)基本语法
  • 键值对:k: v(冒号后面必须加空格,否则语法错误)。
  • 层级关系:用缩进表示(只能用空格,不能用 Tab,缩进量不限,只要层级一致)。
  • 大小写敏感:portPort 是两个不同的配置项。
java 复制代码
server:
  port: 8081
  servlet:
    context-path: /demo # 访问根路径为 http://localhost:8081/demo
(2)值的写法
① 字面量(数字、字符串、布尔)
  • 字符串默认不用加引号。
  • 双引号(""):不转义特殊字符(比如 \n 会被解析为换行)。
    • 示例:name: "zhangsan\nlisi" → 输出:zhangsan 换行 lisi。
  • 单引号(''):转义特殊字符(比如 \n 会被解析为字符串 \n)。
    • 示例:name: 'zhangsan\nlisi' → 输出:zhangsan\nlisi。
② 对象 / Map(键值对集合)

两种写法:

  • 多行写法(推荐,清晰):

    person:
    lastName: 张三
    age: 18
    boss: false

  • 行内写法:

    person: {lastName: 张三, age: 18, boss: false}

③ 数组 / List(有序集合)

两种写法:

  • 多行写法(推荐):

    pets:
    - cat
    - dog
    - pig

  • 行内写法:

    pets: [cat, dog, pig]

3. 配置文件加载位置(优先级)

Spring Boot 会扫描以下 4 个位置的配置文件,优先级从高到低(高优先级配置会覆盖低优先级,低优先级配置会补充高优先级):

  1. 项目根目录 /config/ 下(file:./config/)。
  2. 项目根目录下(file:./)。
  3. classpath:/config/ 下(src/main/resources/config/)。
  4. classpath:/ 下(src/main/resources/)。

实用场景

  • 开发环境:在 src/main/resources/application.yml 中配置开发环境(端口 8081)。
  • 生产环境:在服务器上 Jar 包同级目录的 config/application.yml 中配置生产环境(端口 80),无需修改代码,直接覆盖开发环境配置。

二、高级特性:开发效率翻倍

1. 热部署:修改代码不用重启项目

每次修改代码后,手动重启项目很麻烦。Spring Boot 提供了 spring-boot-devtools 模块,支持热部署(修改代码后自动刷新,无需重启)。

步骤 1:导入依赖

pom.xml 中添加:

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional> <!-- 表示只在开发环境生效 -->
</dependency>
步骤 2:使用热部署
  • 修改 Java 代码、配置文件或静态资源后,按 Ctrl + F9(IDEA),项目会自动热部署。
  • 注意:热部署仅用于开发环境,生产环境需禁用(避免不必要的性能损耗)。

2. 配置文件值注入:绑定配置到 Java 类

如果配置项很多(比如数据库连接信息、第三方 API 密钥),一个个用 @Value 获取太麻烦。Spring Boot 提供了 @ConfigurationProperties 注解,可将配置文件中的属性批量绑定到 Java 类中。

示例:绑定 person 相关配置
步骤 1:编写 YAML 配置

application.yml 中添加:

复制代码
person:
  lastName: 张三
  age: 18
  boss: false
  birth: 2017/12/12
  maps: {k1: v1, k2: 123}
  lists: [李四, 王五]
  dog:
    name: 小狗
    age: 2
步骤 2:编写 Java 类(Person)
java 复制代码
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Component:将类交给 Spring 管理(必须是容器中的组件,才能使用 @ConfigurationProperties)
 * @ConfigurationProperties(prefix = "person"):绑定配置文件中 prefix 为 person 的属性
 */
@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 方法(IDEA 中按 Alt + Insert 快速生成)
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    // 其他 getter/setter 方法省略...

    // 内部类 Dog(也可单独创建)
    public static class Dog {
        private String name;
        private Integer age;
        // getter/setter 方法省略...
    }
}
步骤 3:测试配置注入

在 Controller 中注入 Person 类,访问接口查看结果:

java 复制代码
@Controller
public class PersonController {

    @Autowired // 从 Spring 容器中获取 Person 实例
    private Person person;

    @ResponseBody
    @RequestMapping("/person")
    public Person getPerson() {
        return person;
    }
}

访问 http://localhost:8080/person,会返回配置文件中的数据(JSON 格式),说明配置注入成功。

步骤 4:添加配置提示(可选)

导入配置文件处理器依赖后,编写配置时会有自动提示(避免写错属性名):

XML 复制代码
<!-- 配置文件处理器,编写配置时会有提示 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-configuration-processor</artifactId>
    <optional>true</optional>
</dependency>

3. @Value 与 @ConfigurationProperties 对比

有时候我们只需要获取单个配置项,可使用 @Value 注解。两者对比:

特性 @ConfigurationProperties @Value
功能 批量注入配置文件属性 单个注入配置项
松散绑定 支持(如配置文件中 last-name 可绑定 lastName 不支持
SpEL 表达式 不支持 支持(如 @Value("#{11*2}")
JSR303 数据校验 支持(如 @Email 校验邮箱格式) 不支持
复杂类型封装 支持(Map、List、对象) 不支持

使用场景

  • 批量绑定配置项(如 Person 类)→ 用 @ConfigurationProperties
  • 单个配置项(如 server.port)→ 用 @Value(示例:@Value("${server.port}") private Integer port;)。

4. 配置文件占位符:动态配置

配置文件支持占位符,可实现动态配置(比如引用其他配置项、生成随机数)。

示例(application.properties):
复制代码
# 引用前面定义的配置项
person.dog.name=${person.lastName}_dog
# 若 person.hello 未定义,默认值为 "hello"
person.dog.nickname=${person.hello:hello}_nickname
# 生成随机 UUID
person.uuid=${random.uuid}
# 生成随机整数(10-100 之间)
person.score=${random.int[10,100]}
# 生成随机长整数
person.phone=${random.long}

5. 多环境配置(Profile)

实际开发中,开发环境、测试环境、生产环境的配置不同(比如数据库地址、端口)。使用 Profile 可以快速切换环境,无需修改配置文件内容。

方式 1:多配置文件(推荐)

创建多个配置文件,文件名格式为 application-{profile}.properties/yml

  • application-dev.yml:开发环境(端口 8081)。
  • application-test.yml:测试环境(端口 8082)。
  • application-prod.yml:生产环境(端口 80)。

application.yml 中指定激活的环境:

复制代码
spring:
  profiles:
    active: dev # 激活开发环境,可改为 test 或 prod 切换环境
方式 2:YAML 多文档块(单文件)

在一个 application.yml 中用 --- 分隔多个环境,无需创建多个文件:

复制代码
# 公共配置(所有环境共享)
spring:
  profiles:
    active: prod # 激活生产环境

---
# 开发环境
server:
  port: 8081
spring:
  profiles: dev # 标记为开发环境

---
# 测试环境
server:
  port: 8082
spring:
  profiles: test # 标记为测试环境

---
# 生产环境
server:
  port: 80
spring:
  profiles: prod # 标记为生产环境
切换环境的其他方式(部署时常用)
  • 命令行参数(部署时动态指定):

    java -jar spring-boot-demo.jar --spring.profiles.active=prod

  • 虚拟机参数(IDEA 中测试):在 Run Configurations -> VM options 中添加:-Dspring.profiles.active=test

6. 外部配置加载顺序(部署时重点)

Spring Boot 支持从多个位置加载配置,优先级从高到低(高优先级覆盖低优先级,低优先级补充高优先级)。实际部署时,常用前 3 种方式:

优先级 配置来源 示例
1 命令行参数 java -jar demo.jar --server.port=8088
2 操作系统环境变量 系统中配置 SPRING_DATASOURCE_URL=jdbc:mysql://localhost:3306/prod
3 Jar 包外部的 application-{profile}.yml 服务器上 Jar 包同级目录的 config/application-prod.yml
4 Jar 包内部的 application-{profile}.yml 项目中 resources/application-prod.yml
5 Jar 包外部的 application.yml 服务器上 Jar 包同级目录的 application.yml
6 Jar 包内部的 application.yml 项目中 resources/application.yml

实用场景:项目打包后,无需重新打包,可通过命令行参数或外部配置文件修改配置(比如修改端口、数据库地址)。

示例:部署时修改端口和数据库地址:

复制代码
java -jar spring-boot-demo.jar \
--server.port=80 \
--spring.datasource.url=jdbc:mysql://192.168.1.100:3306/prod \
--spring.datasource.username=prod_user \
--spring.datasource.password=prod_pwd

三、自动配置原理详解:为什么不用写配置?

这是 Spring Boot 最核心、最难理解的部分。在上篇博客中我们简单提到了自动配置的表层原理,本篇将深入拆解,让你明白 "自动配置" 到底是怎么工作的。

1. 自动配置的核心流程(完整版)

  1. 启动主程序 :Spring Boot 启动时,加载主配置类(@SpringBootApplication 标注的类)。
  2. 开启自动配置@SpringBootApplication 包含 @EnableAutoConfiguration 注解,开启自动配置。
  3. 导入自动配置类@EnableAutoConfiguration 触发 SpringFactoriesLoader.loadFactoryNames() 方法,扫描所有 Jar 包下的 META-INF/spring.factories 文件,读取 EnableAutoConfiguration 对应的所有自动配置类(比如 HttpEncodingAutoConfigurationDataSourceAutoConfiguration),并导入 Spring 容器。
  4. 自动配置类生效 :每个自动配置类会根据 "条件注解" 判断是否生效(比如 @ConditionalOnWebApplication 表示仅 Web 应用生效)。
  5. 配置组件属性 :生效的自动配置类会给 Spring 容器添加核心组件(比如 DispatcherServletCharacterEncodingFilter),组件的属性值来自对应的 xxxProperties 类(比如 HttpEncodingProperties),而 xxxProperties 类的属性与配置文件绑定(比如 spring.http.encoding.charset)。
  6. 用户配置覆盖默认配置 :用户在配置文件中修改的属性(比如 spring.http.encoding.charset=UTF-8)会覆盖 xxxProperties 类的默认值,从而实现自定义配置。

2. 条件注解:自动配置类的 "开关"

自动配置类之所以能 "按需生效",全靠 @Conditional 及其派生注解。这些注解会判断当前环境是否满足条件,只有满足条件,自动配置类才会生效。

常用派生注解(通俗解释):

注解 作用 示例场景
@ConditionalOnWebApplication 当前是 Web 应用 仅 Web 项目加载 Tomcat 配置
@ConditionalOnClass 项目中存在指定类 导入 spring-boot-starter-web 后,才加载 Spring MVC 配置
@ConditionalOnMissingClass 项目中不存在指定类 若未导入 Redis 依赖,不加载 Redis 配置
@ConditionalOnBean 容器中存在指定 Bean 只有存在 DataSource Bean,才加载数据库事务配置
@ConditionalOnMissingBean 容器中不存在指定 Bean 若用户未自定义 RedisTemplate,则加载默认 RedisTemplate
@ConditionalOnProperty 配置文件中存在指定属性 配置 spring.http.encoding.enabled=true 才加载编码配置

3. 案例解析:HttpEncodingAutoConfiguration(HTTP 编码自动配置)

HttpEncodingAutoConfiguration 为例,看看自动配置类是如何工作的:

java 复制代码
@Configuration // 标记为配置类
@EnableConfigurationProperties(HttpEncodingProperties.class) // 绑定配置文件属性到 HttpEncodingProperties
@ConditionalOnWebApplication // 仅 Web 应用生效
@ConditionalOnClass(CharacterEncodingFilter.class) // 项目中存在 CharacterEncodingFilter 类才生效
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) // 配置文件中 spring.http.encoding.enabled 为 true 或未配置时生效
public class HttpEncodingAutoConfiguration {

    // HttpEncodingProperties 已与配置文件绑定,从容器中获取实例
    private final HttpEncodingProperties properties;

    // 构造器注入 HttpEncodingProperties
    public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
        this.properties = properties;
    }

    // 给容器添加 CharacterEncodingFilter 组件(解决乱码)
    @Bean
    @ConditionalOnMissingBean(CharacterEncodingFilter.class) // 容器中不存在该 Bean 时才添加
    public CharacterEncodingFilter characterEncodingFilter() {
        CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
        filter.setEncoding(this.properties.getCharset().name()); // 从配置文件获取编码格式
        filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
        filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
        return filter;
    }
}

对应的 HttpEncodingProperties 类(与配置文件绑定):

java 复制代码
@ConfigurationProperties(prefix = "spring.http.encoding") // 绑定配置文件中 prefix 为 spring.http.encoding 的属性
public class HttpEncodingProperties {
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); // 默认编码 UTF-8

    private Charset charset = DEFAULT_CHARSET;
    private Boolean force = true;
    // getter/setter 方法省略...
}

总结该案例的工作流程

  1. 项目导入 spring-boot-starter-web 依赖,存在 CharacterEncodingFilter 类,且是 Web 应用,所以 HttpEncodingAutoConfiguration 生效。
  2. HttpEncodingProperties 与配置文件的 spring.http.encoding 前缀绑定,默认编码为 UTF-8。
  3. 自动配置类给容器添加 CharacterEncodingFilter 组件,组件的编码格式来自 HttpEncodingProperties
  4. 用户可在配置文件中修改 spring.http.encoding.charset=GBK,覆盖默认值,实现自定义编码。

4. 如何查看哪些自动配置类生效?

在配置文件中添加 debug=true,启动项目后,控制台会输出 "自动配置报告",清晰显示哪些自动配置类生效、哪些未生效:

复制代码
debug: true

报告分为两部分:

  • Positive matches:生效的自动配置类(比如 DispatcherServletAutoConfiguration 表示 Spring MVC 核心配置生效)。
  • Negative matches:未生效的自动配置类(比如 ActiveMQAutoConfiguration 因为未导入 ActiveMQ 依赖,所以未生效)。

四、补充知识点:兼容传统 Spring 配置

1. @PropertySource:加载自定义配置文件

默认情况下,@ConfigurationProperties 只会读取 application.properties/yml 全局配置文件。如果想加载自定义配置文件(比如 person.properties),可使用 @PropertySource 注解:

示例:
  • src/main/resources 下创建 person.properties

    person.lastName=李四
    person.age=20
    person.dog.name=旺财

  • Person 类上添加注解:

java 复制代码
@Component
@ConfigurationProperties(prefix = "person")
@PropertySource(value = "classpath:person.properties") // 加载自定义配置文件
public class Person {
    // 属性、getter/setter 省略...
}
  • 注意:@PropertySource 只能加载 properties 文件,不能加载 yml 文件。

2. @ImportResource:导入 Spring 传统 XML 配置

Spring Boot 不推荐使用 XML 配置,但如果有遗留的 XML 配置文件(比如 beans.xml),可使用 @ImportResource 注解让其生效:

示例:
  • 编写 Spring XML 配置文件 src/main/resources/beans.xml
XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义一个 Bean -->
    <bean id="helloService" class="com.example.service.HelloService"/>
</beans>
  • 在主配置类上添加注解:

    @SpringBootApplication
    @ImportResource(locations = "classpath:beans.xml") // 导入 XML 配置文件
    public class SpringBootDemoApplication {
    public static void main(String[] args) {
    SpringApplication.run(SpringBootDemoApplication.class, args);
    }
    }

  • 推荐方案:Spring Boot 推荐用 全注解方式 替代 XML(@Configuration + @Bean),示例如下:

java 复制代码
@Configuration // 替代 XML 配置文件
public class MyConfig {

    // @Bean 替代 <bean> 标签,将方法返回值添加到 Spring 容器
    @Bean
    public HelloService helloService() {
        return new HelloService();
    }
}

五、常见问题避坑指南

  1. 配置文件乱码 :IDEA 中进入 File -> Settings -> Editor -> File Encodings,把 Default encoding for properties files 改成 UTF-8,并勾选 Transparent native-to-ascii conversion
  2. @ConfigurationProperties 绑定失败
    • 类必须加 @Component 注解(成为 Spring 容器组件)。
    • 必须提供 getter/setter 方法。
    • 配置文件中属性名与类中属性名一致(支持松散绑定,如 last-name 对应 lastName)。
  3. 热部署不生效
    • 确认 spring-boot-devtools 依赖已添加。
    • IDEA 中开启自动编译:File -> Settings -> Build, Execution, Deployment -> Compiler,勾选 Build project automatically
    • 修改代码后按 Ctrl + F9 触发热部署。
  4. 静态资源无法访问 :静态资源必须放在 resources/static 目录下(比如 static/js/jquery.js,访问路径为 http://localhost:8080/js/jquery.js)。
  5. 端口被占用 :修改 server.port 为其他端口(比如 8081、8082)。

六、总结

本篇博客深入讲解了 Spring Boot 的核心配置和高级特性,包括配置文件(properties/yml)、热部署、配置注入、多环境配置、自动配置原理等,覆盖了实际开发中的大部分场景。

Spring Boot 的核心思想是 "约定优于配置",通过自动配置和依赖管理,让开发者无需关注框架细节,专注于业务逻辑。掌握了这些知识点,你已经能够独立开发 Spring Boot 项目,并应对各种常见问题。

相关推荐
小北方城市网3 小时前
Redis 分布式锁高可用实现:从原理到生产级落地
java·前端·javascript·spring boot·redis·分布式·wpf
六义义4 小时前
java基础十二
java·数据结构·算法
毕设源码-钟学长5 小时前
【开题答辩全过程】以 基于SpringBoot的智能书城推荐系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
笨手笨脚の5 小时前
深入理解 Java 虚拟机-03 垃圾收集
java·jvm·垃圾回收·标记清除·标记复制·标记整理
莫问前路漫漫5 小时前
WinMerge v2.16.41 中文绿色版深度解析:文件对比与合并的全能工具
java·开发语言·python·jdk·ai编程
九皇叔叔6 小时前
【03】SpringBoot3 MybatisPlus BaseMapper 源码分析
java·开发语言·mybatis·mybatis plus
挖矿大亨6 小时前
c++中的函数模版
java·c++·算法
a程序小傲6 小时前
得物Java面试被问:RocketMQ的消息轨迹追踪实现
java·linux·spring·面试·职场和发展·rocketmq·java-rocketmq
青春男大6 小时前
Redis和RedisTemplate快速上手
java·数据库·redis·后端·spring·缓存
Ghost Face...7 小时前
i386 CPU页式存储管理深度解析
java·linux·服务器