目录
[一、Bean 的作用域:控制 Bean 的创建与复用规则](#一、Bean 的作用域:控制 Bean 的创建与复用规则)
[1.1 作用域的核心概念](#1.1 作用域的核心概念)
[1.2 Spring 的 6 种 Bean 作用域](#1.2 Spring 的 6 种 Bean 作用域)
[1.3 作用域的代码实现与测试](#1.3 作用域的代码实现与测试)
[1.3.1 定义不同作用域的 Bean](#1.3.1 定义不同作用域的 Bean)
[1.3.2 测试不同作用域的 Bean 特性](#1.3.2 测试不同作用域的 Bean 特性)
[1.4 作用域选择原则](#1.4 作用域选择原则)
[二、Bean 的生命周期:从创建到销毁的完整链路](#二、Bean 的生命周期:从创建到销毁的完整链路)
[2.1 生命周期的 5 个核心阶段](#2.1 生命周期的 5 个核心阶段)
[2.2 生命周期详细流程与代码演示](#2.2 生命周期详细流程与代码演示)
[2.2.1 自定义 Bean 生命周期演示类](#2.2.1 自定义 Bean 生命周期演示类)
[2.2.2 配置依赖注入与测试](#2.2.2 配置依赖注入与测试)
[2.2.3 执行结果与流程分析](#2.2.3 执行结果与流程分析)
[2.3 生命周期核心扩展点详解](#2.3 生命周期核心扩展点详解)
[1. Aware 接口系列(初始化阶段回调)](#1. Aware 接口系列(初始化阶段回调))
[2. BeanPostProcessor(Bean 后置处理器)](#2. BeanPostProcessor(Bean 后置处理器))
[3. 初始化 / 销毁方法的 3 种配置方式](#3. 初始化 / 销毁方法的 3 种配置方式)
[2.4 生命周期源码核心逻辑](#2.4 生命周期源码核心逻辑)
[三、Spring Boot 自动配置原理:约定优于配置的底层实现](#三、Spring Boot 自动配置原理:约定优于配置的底层实现)
[3.1 自动配置的核心入口:@SpringBootApplication 注解](#3.1 自动配置的核心入口:@SpringBootApplication 注解)
[3.2 自动配置的核心逻辑:@EnableAutoConfiguration](#3.2 自动配置的核心逻辑:@EnableAutoConfiguration)
[1. 注解拆解](#1. 注解拆解)
[2. 关键步骤详解](#2. 关键步骤详解)
[步骤 1:@AutoConfigurationPackage------ 扫描本地组件](#步骤 1:@AutoConfigurationPackage—— 扫描本地组件)
[步骤 2:AutoConfigurationImportSelector------ 加载第三方配置类](#步骤 2:AutoConfigurationImportSelector—— 加载第三方配置类)
[示例:Redis 的自动配置](#示例:Redis 的自动配置)
[3. 动态加载的核心:@Conditional 系列注解](#3. 动态加载的核心:@Conditional 系列注解)
[3.3 自动配置的完整流程总结](#3.3 自动配置的完整流程总结)
[3.4 手动模拟自动配置:理解第三方依赖的加载逻辑](#3.4 手动模拟自动配置:理解第三方依赖的加载逻辑)
[1. 第三方依赖中定义配置类](#1. 第三方依赖中定义配置类)
[2. 第三方提供 @EnableXXX 注解](#2. 第三方提供 @EnableXXX 注解)
[3. 应用程序启用注解](#3. 应用程序启用注解)
[4.1 核心知识点梳理](#4.1 核心知识点梳理)
[4.2 实战避坑建议](#4.2 实战避坑建议)
前言
Spring 框架是 Java 后端开发的基石,其核心在于IoC(控制反转)容器对 Bean 的高效管理,以及 Spring Boot 基于此实现的 "约定优于配置" 自动配置机制。理解 Bean 的作用域、生命周期,以及 Spring Boot 自动配置的底层逻辑,是写出高效、稳健 Spring 应用的关键。本文将从实战出发,结合源码与代码示例,全面拆解这三大核心知识点,帮你彻底掌握 Spring 的底层工作原理。
一、Bean 的作用域:控制 Bean 的创建与复用规则
在 Spring IoC 容器中,Bean 的作用域定义了Bean 实例的创建时机、存活范围和复用规则。默认情况下,Spring 容器中的 Bean 是单例的(全局唯一),但在不同业务场景下,我们需要灵活调整 Bean 的作用域以满足需求。
1.1 作用域的核心概念
Bean 的作用域本质是 Spring 容器对 Bean 实例的 "管理策略"。比如:
- 单例 Bean(默认):容器启动时创建,全局唯一,所有组件共享同一个实例;
- 多例 Bean:每次获取时创建新实例,组件间互不干扰。
通过一个简单测试即可验证默认单例特性:
java
@SpringBootTest
class DemoApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
void testSingleton() {
// 两次从容器中获取Bean
Dog dog1 = applicationContext.getBean(Dog.class);
Dog dog2 = applicationContext.getBean(Dog.class);
System.out.println(dog1 == dog2); // 输出true,证明是同一个实例
}
}
单例 Bean 的优势是节省内存、创建效率高,但存在线程安全风险(若 Bean 包含可修改的成员变量,多线程并发修改会导致数据错乱)。因此,Spring 提供了 6 种作用域,适配不同场景。
1.2 Spring 的 6 种 Bean 作用域
Spring 支持 6 种作用域,其中后 4 种仅在Web 环境(Spring MVC) 中生效:
| 作用域 | 核心定义 | 适用场景 | 生命周期范围 |
|---|---|---|---|
| singleton | 容器内同名称 Bean 唯一实例(默认) | 无状态 Bean(如 Service、Dao 层) | 容器启动 → 容器销毁 |
| prototype | 每次获取 Bean 时创建新实例 | 有状态 Bean(如包含用户会话数据的对象) | 调用 getBean () → 垃圾回收 |
| request | 每个 HTTP 请求生命周期内创建新实例,请求结束后销毁 | 存储单次请求的临时数据(如请求上下文) | HTTP 请求开始 → 请求结束 |
| session | 每个 HTTP Session 生命周期内创建新实例,会话失效后销毁 | 存储用户会话数据(如登录状态) | 用户登录 → 会话过期 / 退出登录 |
| application | 每个 ServletContext(Web 应用)生命周期内创建新实例,应用停止后销毁 | 存储全局应用数据(如系统配置) | 应用启动 → 应用停止 |
| websocket | 每个 WebSocket 连接生命周期内创建新实例,连接关闭后销毁 | 实时通信场景(如聊天功能) | WebSocket 连接建立 → 连接关闭 |
关键区别说明:
- singleton vs application :两者都是 "全局单例",但 singleton 是IoC 容器级别的单例 (一个应用可有多个 IoC 容器),application 是ServletContext 级别的单例(整个 Web 应用唯一);
- request/session/application :需依赖 Web 环境,且需通过
proxyMode = ScopedProxyMode.TARGET_CLASS生成动态代理(Spring 自动为@RequestScope等注解配置),否则会因 Bean 创建时机问题报错。
1.3 作用域的代码实现与测试
1.3.1 定义不同作用域的 Bean
通过@Scope注解指定 Bean 的作用域,支持直接使用常量或字符串:
java
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.web.context.annotation.RequestScope;
import org.springframework.web.context.annotation.SessionScope;
@Component
public class DogBeanConfig {
// 默认单例(可省略@Scope注解)
@Bean
public Dog defaultDog() {
Dog dog = new Dog();
dog.setName("旺旺");
return dog;
}
// 显式声明单例
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public Dog singleDog() {
return new Dog();
}
// 多例模式
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public Dog prototypeDog() {
return new Dog();
}
// Web环境:请求作用域
@Bean
@RequestScope
public Dog requestDog() {
return new Dog();
}
// Web环境:会话作用域
@Bean
@SessionScope
public Dog sessionDog() {
return new Dog();
}
}
1.3.2 测试不同作用域的 Bean 特性
通过 Controller 测试 Bean 的创建规则:
java
@RestController
@RequestMapping("/scope")
public class ScopeController {
@Autowired
private ApplicationContext applicationContext;
// 测试单例:多次请求返回同一个实例
@GetMapping("/singleton")
public String testSingleton() {
Dog dog1 = applicationContext.getBean("singleDog", Dog.class);
Dog dog2 = applicationContext.getBean("singleDog", Dog.class);
return "单例Bean是否相同:" + (dog1 == dog2); // 始终返回true
}
// 测试多例:每次获取返回新实例
@GetMapping("/prototype")
public String testPrototype() {
Dog dog1 = applicationContext.getBean("prototypeDog", Dog.class);
Dog dog2 = applicationContext.getBean("prototypeDog", Dog.class);
return "多例Bean是否相同:" + (dog1 == dog2); // 始终返回false
}
// 测试请求作用域:同一请求内实例相同,不同请求不同
@GetMapping("/request")
public String testRequest() {
Dog dog1 = applicationContext.getBean("requestDog", Dog.class);
Dog dog2 = applicationContext.getBean("requestDog", Dog.class);
return "同一请求内Bean是否相同:" + (dog1 == dog2); // 同一请求返回true,不同请求返回false
}
}
1.4 作用域选择原则
- 无状态 Bean(推荐 singleton):Service、Dao、工具类等不存储实例级变量的 Bean,单例模式可节省资源;
- 有状态 Bean(推荐 prototype):如用户请求上下文、包含临时数据的对象,多例模式避免线程安全问题;
- Web 场景 :
- 单次请求数据 → request 作用域;
- 用户会话数据 → session 作用域;
- 应用全局数据 → application 作用域。
二、Bean 的生命周期:从创建到销毁的完整链路
Bean 的生命周期是 Spring 管理 Bean 的核心流程,描述了 Bean 从 "诞生"(实例化)到 "消亡"(销毁)的全生命周期。理解生命周期,能帮助我们在关键节点扩展 Bean 的功能(如初始化时加载配置、销毁时释放资源)。
2.1 生命周期的 5 个核心阶段
Spring Bean 的生命周期可概括为 5 个阶段,每个阶段都包含可扩展的 "钩子函数":
| 阶段 | 核心操作 | 扩展点(自定义逻辑) |
|---|---|---|
| 1. 实例化 | 为 Bean 分配内存空间,创建原始对象(调用构造函数) | - |
| 2. 属性赋值 | 注入 Bean 的依赖(@Autowired、Setter 方法、构造函数注入) | - |
| 3. 初始化 | 初始化 Bean(回调 Aware 接口、执行自定义初始化方法、BeanPostProcessor 增强) | Aware 接口、@PostConstruct、init-method、BeanPostProcessor |
| 4. 使用 Bean | 应用程序调用 Bean 的业务方法 | - |
| 5. 销毁 Bean | 容器关闭时释放 Bean 资源 | @PreDestroy、destroy-method、DisposableBean 接口 |
用 "买房入住" 的流程类比:
- 实例化 → 买房(获得房屋主体,从无到有);
- 属性赋值 → 装修(配置房屋设施,注入 "依赖");
- 初始化 → 购置家电、家具(完成初始化,可入住);
- 使用 Bean → 入住(正常使用房屋功能);
- 销毁 Bean → 卖房(释放房屋资源)。
2.2 生命周期详细流程与代码演示
2.2.1 自定义 Bean 生命周期演示类
通过实现 Aware 接口、添加注解等方式,演示生命周期各阶段的执行顺序:
java
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;
@Component
public class BeanLifeComponent implements BeanNameAware {
// 模拟依赖注入的属性
private String appName;
// 1. 实例化:调用构造函数
public BeanLifeComponent() {
System.out.println("阶段1:实例化 → 执行构造函数");
}
// 2. 属性赋值:通过Setter方法注入依赖(@Autowired也可)
public void setAppName(String appName) {
this.appName = appName;
System.out.println("阶段2:属性赋值 → 注入appName:" + appName);
}
// 3. 初始化前:Aware接口回调(获取Bean名称、容器信息等)
@Override
public void setBeanName(String beanName) {
System.out.println("阶段3-1:初始化 → Aware接口回调(Bean名称:" + beanName + ")");
}
// 3. 初始化:@PostConstruct注解(JDK标准注解,初始化时执行)
@PostConstruct
public void postConstruct() {
System.out.println("阶段3-2:初始化 → @PostConstruct注解执行(初始化逻辑)");
}
// 4. 使用Bean:业务方法
public void doBusiness() {
System.out.println("阶段4:使用Bean → 执行业务方法(appName:" + appName + ")");
}
// 5. 销毁Bean:@PreDestroy注解(容器关闭时执行)
@PreDestroy
public void preDestroy() {
System.out.println("阶段5:销毁Bean → @PreDestroy注解执行(释放资源)");
}
}
2.2.2 配置依赖注入与测试
java
// 配置类:注入appName属性
@Configuration
public class LifeConfig {
@Bean
public BeanLifeComponent beanLifeComponent() {
BeanLifeComponent component = new BeanLifeComponent();
component.setAppName("SpringDemo"); // 属性赋值
return component;
}
}
// 测试类
@SpringBootTest
class LifeCycleTest {
@Autowired
private BeanLifeComponent beanLifeComponent;
@Test
void testLifeCycle() {
// 4. 使用Bean
beanLifeComponent.doBusiness();
}
}
2.2.3 执行结果与流程分析
运行测试类,控制台输出如下(顺序严格遵循生命周期):
阶段1:实例化 → 执行构造函数
阶段2:属性赋值 → 注入appName:SpringDemo
阶段3-1:初始化 → Aware接口回调(Bean名称:beanLifeComponent)
阶段3-2:初始化 → @PostConstruct注解执行(初始化逻辑)
阶段4:使用Bean → 执行业务方法(appName:SpringDemo)
阶段5:销毁Bean → @PreDestroy注解执行(释放资源)
2.3 生命周期核心扩展点详解
除了上述演示的@PostConstruct和BeanNameAware,Spring 还提供了多个强大的生命周期扩展点:
1. Aware 接口系列(初始化阶段回调)
用于获取 Spring 容器的核心信息,常用接口:
BeanNameAware:获取 Bean 在容器中的名称;BeanFactoryAware:获取 BeanFactory 容器实例;ApplicationContextAware:获取 ApplicationContext 容器实例;BeanClassLoaderAware:获取 Bean 的类加载器。
2. BeanPostProcessor(Bean 后置处理器)
全局生效的 Bean 增强器,可在所有 Bean 的初始化前后执行自定义逻辑(无需 Bean 自身实现接口):
java
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
// 初始化前执行
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof BeanLifeComponent) {
System.out.println("BeanPostProcessor:初始化前增强 → " + beanName);
}
return bean;
}
// 初始化后执行
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof BeanLifeComponent) {
System.out.println("BeanPostProcessor:初始化后增强 → " + beanName);
}
return bean;
}
}
添加后,执行结果会新增两行输出,体现全局增强能力。
3. 初始化 / 销毁方法的 3 种配置方式
| 配置方式 | 示例代码 |
|---|---|
| 注解方式 | @PostConstruct(初始化)、@PreDestroy(销毁) |
| 接口方式 | 实现InitializingBean(重写afterPropertiesSet)、DisposableBean(重写destroy) |
| XML 配置方式 | <bean init-method="init" destroy-method="destroy"/>(Spring Boot 中极少使用) |
2.4 生命周期源码核心逻辑
Spring 创建 Bean 的核心入口在AbstractAutowireCapableBeanFactory类的doCreateBean方法,该方法封装了 "实例化→属性赋值→初始化" 的核心流程:
java
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// 1. 实例化Bean(创建原始对象)
BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args);
// 2. 属性赋值(注入依赖)
populateBean(beanName, mbd, instanceWrapper);
// 3. 初始化Bean(回调Aware、执行初始化方法、BeanPostProcessor增强)
exposedObject = initializeBean(beanName, exposedObject, mbd);
return exposedObject;
}
通过源码可见,Spring 的生命周期是通过 "模板方法模式" 实现的,固定流程不变,扩展点开放给开发者自定义逻辑。
三、Spring Boot 自动配置原理:约定优于配置的底层实现
Spring Boot 的核心优势是 "自动配置"------ 无需手动编写大量 XML 或 Java 配置,仅需引入依赖,Spring Boot 就能自动加载所需的 Bean 和配置。这一切的底层逻辑,都源于@SpringBootApplication注解。
3.1 自动配置的核心入口:@SpringBootApplication 注解
Spring Boot 启动类的唯一标识是@SpringBootApplication,它是一个组合注解,封装了 3 个核心注解:
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 1. 标识为配置类
@EnableAutoConfiguration // 2. 开启自动配置(核心)
@ComponentScan(excludeFilters = { ... }) // 3. 包扫描
public @interface SpringBootApplication {
// 省略属性...
}
三个注解的核心作用:
@SpringBootConfiguration:本质是@Configuration,标识当前类是 Spring 的配置类,可定义@Bean;@ComponentScan:默认扫描启动类所在包及其子包 下的@Component、@Service、@Controller等注解,将其注册为 Bean;@EnableAutoConfiguration:自动配置的核心,负责加载第三方依赖的配置类(如 MyBatis、Redis 的自动配置)。
3.2 自动配置的核心逻辑:@EnableAutoConfiguration
@EnableAutoConfiguration的底层是通过@Import(AutoConfigurationImportSelector.class)实现的,其核心流程如下:
1. 注解拆解
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 1. 自动扫描启动类所在包的组件
@Import(AutoConfigurationImportSelector.class) // 2. 加载自动配置类
public @interface EnableAutoConfiguration {
// 省略属性...
}
2. 关键步骤详解
步骤 1:@AutoConfigurationPackage------ 扫描本地组件
@AutoConfigurationPackage通过@Import(AutoConfigurationPackages.Registrar.class),将启动类所在包下的所有组件注册到 IoC 容器。这也是为什么 Spring Boot 项目的 Bean 通常要放在启动类同级或子包下。
步骤 2:AutoConfigurationImportSelector------ 加载第三方配置类
AutoConfigurationImportSelector是ImportSelector接口的实现类,其核心方法selectImports会:
- 读取所有依赖 Jar 包中
META-INF/spring.factories或META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件; - 这些文件中定义了需要自动配置的类(如
RedisAutoConfiguration、MyBatisAutoConfiguration); - 通过
@Conditional系列注解(如@ConditionalOnClass、@ConditionalOnMissingBean)动态判断是否加载该配置类。
示例:Redis 的自动配置
在spring-boot-autoconfigure.jar的META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中,包含:
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
RedisAutoConfiguration的核心代码:
java
@Configuration
@ConditionalOnClass(RedisOperations.class) // 存在RedisOperations类才加载(即引入了Redis依赖)
@EnableConfigurationProperties(RedisProperties.class) // 绑定application.yml中的Redis配置
public class RedisAutoConfiguration {
// 自动注册RedisTemplate Bean
@Bean
@ConditionalOnMissingBean(name = "redisTemplate") // 容器中没有redisTemplate时才创建
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}
3. 动态加载的核心:@Conditional 系列注解
自动配置并非加载所有配置类,而是通过@Conditional注解动态判断,常用条件注解:
@ConditionalOnClass:类路径下存在指定类才加载(如引入 Redis 依赖才加载RedisAutoConfiguration);@ConditionalOnMissingBean:容器中不存在指定 Bean 才加载(允许用户自定义 Bean 覆盖默认配置);@ConditionalOnProperty:配置文件中存在指定属性才加载(如spring.redis.enabled=true);@ConditionalOnWebApplication:Web 环境下才加载。
3.3 自动配置的完整流程总结
SpringBoot启动 → 加载@SpringBootApplication → 触发@EnableAutoConfiguration
→ AutoConfigurationImportSelector读取META-INF目录下的配置类
→ 通过@Conditional注解动态筛选配置类
→ 加载配置类中的@Bean到IoC容器
→ 应用程序可直接注入使用这些Bean
3.4 手动模拟自动配置:理解第三方依赖的加载逻辑
当我们引入第三方依赖(如自定义框架)时,可模仿 Spring Boot 的自动配置方式,让 Spring 自动加载 Bean:
1. 第三方依赖中定义配置类
java
// 第三方依赖的配置类(包路径:com.bite.autoconfig)
@Component
public class BiteConfig {
public void printInfo() {
System.out.println("第三方依赖的Bean加载成功!");
}
}
2. 第三方提供 @EnableXXX 注解
java
// 第三方依赖的注解(封装@Import)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(BiteConfig.class) // 导入第三方配置类
public @interface EnableBiteConfig {
}
3. 应用程序启用注解
java
// Spring Boot启动类
@SpringBootApplication
@EnableBiteConfig // 启用第三方的自动配置
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(DemoApplication.class, args);
// 直接获取第三方依赖的Bean
BiteConfig biteConfig = context.getBean(BiteConfig.class);
biteConfig.printInfo(); // 输出:第三方依赖的Bean加载成功!
}
}
四、核心知识点总结与实战建议
4.1 核心知识点梳理
| 主题 | 核心结论 |
|---|---|
| Bean 作用域 | 6 种作用域,常用 singleton(无状态)和 prototype(有状态),Web 环境用 request/session |
| Bean 生命周期 | 实例化→属性赋值→初始化→使用→销毁,扩展点:@PostConstruct、BeanPostProcessor、Aware 接口 |
| 自动配置 | 核心是 @EnableAutoConfiguration,通过加载 META-INF 目录下的配置类实现 "约定优于配置" |
| 条件注解 | @Conditional 系列控制配置类动态加载,支持用户自定义 Bean 覆盖默认配置 |
4.2 实战避坑建议
-
Bean 作用域:
- 单例 Bean 避免定义可修改的成员变量,如需存储临时数据,使用 ThreadLocal;
- Web 环境的 request/session 作用域 Bean,若被单例 Bean 依赖,需通过
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)生成代理。
-
Bean 生命周期:
- 初始化逻辑优先使用
@PostConstruct(JDK 标准注解,兼容性好),而非init-method; - 销毁逻辑优先使用
@PreDestroy,用于释放数据库连接、线程池等资源。
- 初始化逻辑优先使用
-
Spring Boot 自动配置:
- 自定义 Bean 可覆盖自动配置(如自定义
RedisTemplate会替代默认实现); - 排除不需要的自动配置:
@SpringBootApplication(exclude = RedisAutoConfiguration.class); - 查看自动配置报告:启动时添加
debug=true,控制台会输出哪些配置类已生效、哪些被排除。
- 自定义 Bean 可覆盖自动配置(如自定义
五、总结
Spring 的核心是 IoC 容器对 Bean 的精细化管理,而 Bean 的作用域和生命周期是管理 Bean 的基础,Spring Boot 的自动配置则是在此基础上的 "开箱即用" 优化。掌握本文的三大知识点:
- 能根据业务场景选择合适的 Bean 作用域,避免线程安全问题;
- 能利用 Bean 生命周期的扩展点,灵活定制 Bean 的初始化和销毁逻辑;
- 能理解 Spring Boot 自动配置的底层逻辑,轻松排查配置冲突、自定义配置。