🌱 10.1.1 创建自己的 FailureAnalyzer(失败分析器)
✅ 是什么?
当你启动 Spring Boot 应用时,如果出现异常(比如端口被占用、配置错误等),默认会打印一大段堆栈信息,对开发者不友好。
FailureAnalyzer 的作用就是:拦截特定的启动异常,将其转换成一条清晰、可读性强的提示信息,方便快速定位问题。
例如:
            
            
              text
              
              
            
          
          ***************************
APPLICATION FAILED TO START
***************************
Description:
The port 8080 is already in use.
Action:
Identify and stop the process that's using port 8080 or configure this application to use another port.
        这就是 PortInUseFailureAnalyzer 的功劳。
🔧 如何自定义?
- 继承 
AbstractFailureAnalyzer<T>- 泛型 T 是你要处理的异常类型。
 - 重写 
analyze()方法,在其中判断是否能处理该异常,能则返回FailureAnalysis对象,否则返回null。 
 
            
            
              java
              
              
            
          
          public class ProjectConstraintViolationFailureAnalyzer 
    extends AbstractFailureAnalyzer<ConstraintViolationException> {
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure, ConstraintViolationException cause) {
        // 分析异常原因并生成友好的提示
        return new FailureAnalysis(
            "A project constraint was violated.",
            "Check your project configuration and fix the constraints.",
            cause
        );
    }
}
        - 注册到 
META-INF/spring.factories 
            
            
              properties
              
              
            
          
          org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.ProjectConstraintViolationFailureAnalyzer
        - 可选:注入 BeanFactory 或 Environment
实现BeanFactoryAware或EnvironmentAware接口即可获取上下文环境。 
💡 小结
- 用途:美化启动报错信息。
 - 原理:通过 SPI 机制加载 
spring.factories注册的分析器。 - 场景:团队内部统一错误提示规范、封装业务校验失败提示。
 
🛠️ 10.1.2 排查自动配置问题(Troubleshoot Auto-configuration)
Spring Boot 的核心魅力在于 自动配置(Auto-Configuration),但有时候某些功能没生效,不知道为什么?这一节教你如何排查。
🔍 核心工具:ConditionEvaluationReport
Spring Boot 在启动过程中会对每个 @ConditionalOnXxx 条件进行评估,最终形成一份报告 ------ ConditionEvaluationReport。
使用方式:
- 
开启 DEBUG 日志
yamllogging: level: org.springframework: DEBUG启动后控制台会输出详细的条件匹配结果。
 - 
使用 Actuator 的
/conditions端点(推荐)添加依赖:
xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>访问接口:
GET /actuator/conditions返回 JSON,展示哪些自动配置类"匹配成功"或"未匹配"。
 
🔎 源码阅读技巧
要搞清楚某个功能是如何自动启用的,请关注以下几类组件:
| 类型 | 示例 | 说明 | 
|---|---|---|
*AutoConfiguration 类 | 
DataSourceAutoConfiguration | 
自动配置主逻辑所在 | 
@Conditional* 注解 | 
@ConditionalOnClass, @ConditionalOnMissingBean | 
控制配置何时生效 | 
@ConfigurationProperties 类 | 
ServerProperties, JpaProperties | 
外部配置绑定入口 | 
@Value | 
@Value("${my.property}") | 
直接读取属性值 | 
Binder.bind() | 
显式从 Environment 绑定对象 | 更灵活的配置读取 | 
@ConditionalOnExpression | 
SpEL 表达式控制开关 | 动态启用功能 | 
🚀 调试技巧
- 
启动参数加
--debug或 JVM 参数-Ddebugbashjava -jar myapp.jar --debug控制台会打印所有自动配置决策过程。
 - 
查看
/actuator/configprops端点:查看所有@ConfigurationProperties配置项的实际值。 
💡 小结
- 排查自动配置问题 = 看条件报告 + 读源码。
 - 推荐组合:Actuator (
/conditions,/configprops) + DEBUG 日志。 - 学会看 
*AutoConfiguration源码是进阶必备技能。 
⚙️ 10.1.3 在上下文创建前自定义 Environment 或 ApplicationContext
有时我们需要在 Spring 容器初始化之前做一些准备工作,比如:
- 加载额外的配置文件(如 YAML、加密配置)
 - 修改环境变量
 - 设置系统属性
 
Spring Boot 提供了两种扩展机制:
方式一:使用 ApplicationContextInitializer
作用:在 ApplicationContext 创建之后、刷新之前执行初始化逻辑。
用法:
- 实现接口:
 
            
            
              java
              
              
            
          
          public class MyContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext ac) {
        System.out.println("Custom init: " + ac.getId());
        // 可以修改 environment、注册 bean factory post processor 等
    }
}
        - 注册方式(三种):
 
- 编程式注册(最常用):
 
            
            
              java
              
              
            
          
          public static void main(String[] args) {
    SpringApplication app = new SpringApplication(MyApp.class);
    app.addInitializers(new MyContextInitializer());
    app.run(args);
}
        - 配置文件注册(全局生效):
 
            
            
              properties
              
              
            
          
          # application.properties
context.initializer.classes=com.example.MyContextInitializer
        - SPI 注册(打包为库时使用):
 
            
            
              properties
              
              
            
          
          # META-INF/spring.factories
org.springframework.context.ApplicationContextInitializer=\
com.example.MyContextInitializer
        方式二:使用 EnvironmentPostProcessor
更早阶段介入 ------ 在 Environment 准备好后、容器创建前。
常用于:加载自定义配置文件(如 config.yml)。
示例:加载 classpath 下的 YAML 文件
            
            
              java
              
              
            
          
          public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor {
    private final YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        Resource path = new ClassPathResource("com/example/myapp/config.yml");
        if (path.exists()) {
            try {
                PropertySource<?> ps = loader.load("custom-yml", path).get(0);
                environment.getPropertySources().addLast(ps); // 最低优先级
            } catch (IOException e) {
                throw new IllegalStateException("Failed to load config.yml", e);
            }
        }
    }
}
        注册:
            
            
              properties
              
              
            
          
          # META-INF/spring.factories
org.springframework.boot.env.EnvironmentPostProcessor=com.example.EnvironmentPostProcessorExample
        ⚠️ 注意:不要用
@PropertySource加载关键配置(如logging.level.*),因为它太晚才生效!
💡 小结
| 扩展点 | 时机 | 典型用途 | 
|---|---|---|
EnvironmentPostProcessor | 
最早,Environment 初始化后 | 加载额外配置文件、解密配置 | 
ApplicationContextInitializer | 
ApplicationContext 创建后 | 修改环境、注册监听器、添加资源 | 
ApplicationListener | 
监听各种 ApplicationEvent | 响应事件(如启动完成) | 
🌐 10.1.4 构建 ApplicationContext 层次结构(父子容器)
Spring 支持多个 ApplicationContext 形成树状结构,父容器可以共享 Bean 给子容器。
📌 典型场景
- Web 应用中,根容器放 Service 层 Bean,子容器放 Controller。
 - 微服务架构中分离核心服务与 UI 模块。
 
✅ 使用 ApplicationBuilder
        
            
            
              java
              
              
            
          
          new ApplicationBuilder()
    .sources(ParentConfig.class)
    .child(WebConfig.class)
    .run(args);
        这会构建一个双层上下文体系:
- 父容器:加载 
ParentConfig - 子容器:加载 
WebConfig,可访问父容器中的 Bean 
更多见官方文档 "Fluent Builder API" 章节。
🖥️ 10.1.5 创建非 Web 应用(Non-web Application)
不是所有 Spring Boot 应用都是 Web 服务!你可以用它做批处理、定时任务、数据迁移等 CLI 工具。
❓ 如何让 Spring Boot 不启动 Web 服务器?
方法一:排除 web 依赖(推荐)
Maven 中不引入:
            
            
              xml
              
              
            
          
          <!-- 不要包含这个 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
        或者只引入非 web starter:
            
            
              xml
              
              
            
          
          <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>
        方法二:显式设置类型
            
            
              java
              
              
            
          
          public static void main(String[] args) {
    SpringApplication app = new SpringApplication(MyApp.class);
    app.setWebApplicationType(WebApplicationType.NONE); // 关键!
    app.run(args);
}
        或通过配置:
            
            
              properties
              
              
            
          
          spring.main.web-application-type=none
        ✅ 如何运行业务逻辑?
实现 CommandLineRunner 接口:
            
            
              java
              
              
            
          
          @Component
public class MyJobRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("开始执行批处理任务...");
        // 你的业务代码
    }
}
        Spring Boot 启动完成后会自动调用 run() 方法。
多个
CommandLineRunner可通过@Order控制执行顺序。
💡 小结
- 非 Web 应用 = 没有嵌入式服务器。
 - 使用 
CommandLineRunner或ApplicationRunner执行主逻辑。 - 适合写脚本、ETL、定时任务、测试工具等。
 
✅ 总结:核心知识点图谱
| 主题 | 关键词 | 用途 | 
|---|---|---|
FailureAnalyzer | 
异常美化 | 提升用户体验,友好提示错误 | 
ConditionEvaluationReport | 
调试 auto-config | 查看哪些自动配置生效了 | 
EnvironmentPostProcessor | 
早期环境定制 | 加载额外配置文件 | 
ApplicationContextInitializer | 
上下文初始化 | 修改 context 或 env | 
ApplicationBuilder | 
构建父子容器 | 分层架构设计 | 
CommandLineRunner | 
非 Web 应用入口 | 写命令行程序、批处理任务 | 
如果你正在学习 Spring Boot 源码或想提升架构能力,建议动手实践以下几个实验:
🔬 动手实验建议
- 写一个 
FailureAnalyzer拦截FileNotFoundException并提示用户检查配置路径。 - 创建一个 
EnvironmentPostProcessor加载extra-config.yaml并验证其优先级。 - 用 
CommandLineRunner写一个定时导出数据库数据的小工具。 - 开启 Actuator 的 
/conditions和/configprops,观察自动配置行为。 
如有需要,我可以为你提供完整的示例项目结构或代码模板 👇
也可以继续深入某一部分(比如 @Conditional 原理、Binder 机制等)。欢迎继续提问!