资讯动态

解救应用启动危机:Spring Boot的FailureAnalyzer机制

发布时间:2026/9/7 23:55:02 来源:尧图企业网站定制
目录一、走进FailureAnalyzer二、在Spring Boot中如何生效三、为什么可能需要自定义FailureAnalyzer四、实现自定义基本步骤一完整步骤要求二注册方式说明通过Spring Boot的spring.factories文件建议方式在启动类中手动注册本人不建议五、实现自定义举例六、一些建议七、总结干货分享感谢您的阅读在程序员的世界里错误就像邻居家的狗总是无时无刻不在你的代码中“汪汪”作响仿佛在说“嘿注意我我又来了”当你优雅地写完一段代码按下回车键心里满是期待时突然跳出来一个神秘的异常这感觉就像是开门见到一只大黑狗——又惊又怕。但是别担心今天我们要介绍的“FailureAnalyzer”就像一位经验丰富的兽医专门为你的代码诊断问题帮你把那些不请自来的错误处理得服服帖帖它不仅能识别出问题所在还能给你提供相应的解决方案让你的代码重新焕发生机宛如一只朝气蓬勃的小狗摇着尾巴跑向美好的明天。在这篇文章中我们将一起探索FailureAnalyzer的奥秘学习如何自定义它以应对那些棘手的错误。准备好了吗让我们带上“程序员的护目镜”一起踏上这场充满挑战和乐趣的冒险之旅吧一、走进FailureAnalyzer想象一下你正在开发一个基于Spring Boot的网络应用程序你已经编写了一大堆代码做了各种配置终于迫不及待地想要启动你的应用程序看看它是不是如你所愿地运行。你兴奋地运行了启动命令但突然间控制台上出现了一堆红色的错误信息。如下可以立刻看到这个报错来自于LoggingFailureAnalysisReporter其内部其实就是Spring Boot中被誉为故障排查神器的工具FailureAnalyzer。你决定让它出马看看能否解决你的问题。你应该感到非常惊讶和兴奋因为FailureAnalyzer不仅仅找出了问题还给出了解决方案按照建议修复了配置再次启动应用程序这一次一切都运行得非常顺利。通过这个简单的场景你立刻感受到了FailureAnalyzer的价值和魔力。它就像是你的应用程序启动的保险让你在遇到问题时能够迅速找出解决方案让你的开发过程更加流畅和高效。二、在Spring Boot中如何生效在Spring Boot的spring.factories文件位于META-INF目录下中已经包含了一些FailureAnalyzer的配置FailureAnalyzer实现类通常在spring.factories文件中被声明以便在应用程序启动时被Spring Boot自动发现并注册。org.springframework.boot.diagnostics.FailureAnalyzer\ org.springframework.boot.autoconfigure.jdbc.DataSourceFailedAnalyzer,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQConnectFailureAnalyzer,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisJmsConnectionFailureAnalyzer,\ org.springframework.boot.autoconfigure.jms.hornetq.HornetQConnectFailureAnalyzer,\ org.springframework.boot.autoconfigure.jms.hornetq.HornetQDependencyExceptionAnalyzer,\ org.springframework.boot.autoconfigure.solr.SolrExceptionAnalyzer,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBeanNotAvailableAnalyzer,\ org.springframework.boot.cloud.CloudPlatformConnectorsFailureAnalyzer,\ org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessorFailureAnalyzer,\ org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorChecker,\ org.springframework.boot.devtools.autoconfigure.DevToolsMissingFilterFailureAnalyzer,\ org.springframework.boot.devtools.autoconfigure.LocalDevToolsAutoConfiguration$LocalDevToolsFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideAnalyzer,\ org.springframework.boot.diagnostics.analyzer.IllegalComponentScanFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidEmbeddedServletContainerConfigurationFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.InvalidTemplateAvailabilityProviderAnalyzer,\ org.springframework.boot.diagnostics.analyzer.NonCompatibleConfigurationClassFailureAnalyzer,\ org.springframework.boot.diagnostics.analyzer.SingleConstructorInjectionAnalyzer当应用程序启动失败时Spring Boot会自动触发这个机制尝试识别和处理启动失败的原因并提供有用的诊断信息和解决方案。三、为什么可能需要自定义FailureAnalyzer当然如果有需要的话我们可以自定义FailureAnalyzer来更灵活地处理应用程序启动失败的情况提供更精准的故障诊断和解决方案等特别是针对做基础架构的同学。理由说明特定错误情况处理默认的FailureAnalyzer无法准确识别或处理特定的错误情况。通过自定义可以针对这些特定的错误情况编写定制化的诊断逻辑提供更精准的故障诊断和解决方案。额外的诊断信息默认的FailureAnalyzer只提供基本的诊断信息但在某些情况下可能需要更多的详细信息来准确诊断问题。通过自定义可添加额外的诊断逻辑收集更多有用的信息来更好地理解问题。集成外部系统应用程序与外部系统集成而启动失败可能是由于与这些外部系统的交互出现问题所致。通过自定义可以集成额外的逻辑例如调用外部API或检查外部系统的状态以诊断和解决与外部系统相关的问题。定制化的解决方案某些错误情况需要特定的解决方案通过自定义可以根据应用程序的特定需求或约束提供定制化的解决方案以更好地满足应用程序的需求。四、实现自定义基本步骤一完整步骤要求要实现自定义的FailureAnalyzer我们需要完成以下步骤自定义异常并创建检查要求规定。创建一个类并实现AbstractFailureAnalyzer接口重写analyze()方法用于分析异常并返回FailureAnalysis对象。将自定义的FailureAnalyzer类注册到Spring Boot应用程序中。注意在 Spring Boot 应用程序中自定义的多个失败分析器在实现上没有固定的先后次序。当应用程序启动时Spring Boot 会自动扫描并注册所有的失败分析器然后按照它们的类名顺序进行调用。这意味着无论你如何组织和编写你的失败分析器类它们都将在应用程序启动时同时注册并且没有先后次序。二注册方式说明要让自定义的FailureAnalyzer生效注册到Spring Boot应用程序中一般有两种方法通过Spring Boot的spring.factories文件建议方式在src/main/resources目录下创建一个名为META-INF/spring.factories的文件如果已存在则跳过此步骤。在spring.factories文件中添加用于实现的自定义FailureAnalyzer类和上文中展示的spring.factories文件中的格式一样。在启动类中手动注册本人不建议在Spring Boot应用程序的启动类SpringBootApplication中手动注册FailureAnalyzerpublic static void main(String[] args) { SpringApplication application new SpringApplication(ZYFApplication.class); application.addListeners(new ConfigFileFailureAnalyzer()); application.run(args); }后续列举一些案例但是情况请依据实际项目需求来定。我一般是以上面建议方式进行写的注册。五、实现自定义举例假设我们的应用程序在启动时需要加载某些特定的配置文件但如果对应配置文件不存在将导致应用程序启动失败。默认的FailureAnalyzer可能无法准确地识别或处理这种特定情况因此我们可以自定义一个FailureAnalyzer来处理这种特定的错误情况。首先定义必要文件未找到异常如下package org.zyf.javabasic.spring.failureanalyzer.exception; /** * program: zyfboot-javabasic * description: ConfigFileNotFoundException * author: zhangyanfeng * create: 2024-05-02 17:25 **/ public class ConfigFileNotFoundException extends RuntimeException { private final String fileNames; public ConfigFileNotFoundException(String fileNames) { super(Configuration file fileNames not found); this.fileNames fileNames; } public String getFileNames() { return fileNames; } }接着创建检查类对我们系统要求的必要文件作出基本的检查并返回要求文件异常基本信息package org.zyf.javabasic.spring.failureanalyzer.checker; import com.google.common.collect.Lists; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Component; import org.zyf.javabasic.spring.failureanalyzer.exception.ConfigFileNotFoundException; import javax.annotation.PostConstruct; import java.util.List; /** * program: zyfboot-javabasic * description: 系统必要配置文件检查 * author: zhangyanfeng * create: 2024-05-02 18:14 **/ Component public class ConfigFileNotFoundChecker { private final ResourceLoader resourceLoader; public ConfigFileNotFoundChecker(ResourceLoader resourceLoader) { this.resourceLoader resourceLoader; } public boolean exists(String fileName) { Resource resource resourceLoader.getResource(classpath: fileName); return resource.exists(); } PostConstruct public void checkConfigFiles() throws ConfigFileNotFoundException { // 要检查的文件列表 ListString filesToCheck Lists.newArrayList(); filesToCheck.add(application.yml); filesToCheck.add(zyf_application_context.xml); filesToCheck.add(report-config.xml); filesToCheck.add(urlzyf.properties); // 存储不存在的文件名 ListString notFoundFiles Lists.newArrayList(); // 检查每个文件是否存在 for (String fileName : filesToCheck) { if (!exists(fileName)) { notFoundFiles.add(fileName); } } // 如果存在未找到的文件则抛出异常 if (!notFoundFiles.isEmpty()) { throw new ConfigFileNotFoundException(notFoundFiles.toString()); } } }接着创建并实现AbstractFailureAnalyzer重写analyze()方法如下package org.zyf.javabasic.spring.failureanalyzer.analyzer; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; import org.zyf.javabasic.spring.failureanalyzer.exception.ConfigFileNotFoundException; import org.zyf.javabasic.spring.failureanalyzer.exception.RequiredPropertyException; /** * program: zyfboot-javabasic * description: 检查必要文件是否存在异常 * author: zhangyanfeng * create: 2024-05-02 18:26 **/ public class ZYFConfigFileFailureAnalyzer extends AbstractFailureAnalyzerConfigFileNotFoundException { Override protected FailureAnalysis analyze(Throwable rootFailure, ConfigFileNotFoundException cause) { String description description(cause); String action action(cause); return new FailureAnalysis(description, action, cause); } private String description(ConfigFileNotFoundException ex) { return String.format(Failed to load configuration file %s., ex.getFileNames()); } private String action(ConfigFileNotFoundException ex) { return String.format(Check if the configuration file:%s exists., ex.getFileNames()); } }spring.factories中增加本次新增验证org.springframework.boot.diagnostics.FailureAnalyzer\ org.zyf.javabasic.spring.failureanalyzer.analyzer.ZYFConfigFileFailureAnalyzer现在其中本地未创建ourlzyf.properties文件故程序启动报错如下符合我们的预期。接着如果要求针对报告配置文件report-config.xml中必须包含数据库连接信息和报告生成器的情况并要求有更加详细和严格的配置文件格式验证接着我们继续定义一个异常类且需要指明异常情况。先定义一个这种类型返回的基本配置错误信息如下package org.zyf.javabasic.spring.failureanalyzer.model; /** * program: zyfboot-javabasic * description: 表示不同的错误类型例如缺少必要属性、属性值格式错误等 * author: zhangyanfeng * create: 2024-05-02 19:57 **/ public class ConfigFileFormatErrorInfo { private final boolean fileNotFound; private final ErrorType errorType; private final String fileName; public ConfigFileFormatErrorInfo(boolean fileNotFound, ErrorType errorType, String fileName) { this.fileNotFound fileNotFound; this.errorType errorType; this.fileName fileName; } public boolean isFileNotFound() { return fileNotFound; } public ErrorType getErrorType() { return errorType; } public String getFileName() { return fileName; } public DescriptionAndAction getDescriptionAndAction() { String description; String action; if (fileNotFound) { description Configuration file fileName not found; action Check if the configuration file exists.; } else { switch (errorType) { case MISSING_PROPERTY: description Missing required property in configuration file fileName ; action Ensure all required properties are provided in the configuration file.; break; case INVALID_VALUE: description Invalid value for property in configuration file fileName ; action Correct the value of the property in the configuration file.; break; case OTHER: default: description Other configuration file format error in file fileName ; action Review the configuration file for formatting issues.; break; } } return new DescriptionAndAction(description, action); } public enum ErrorType { MISSING_PROPERTY, INVALID_VALUE, OTHER } } package org.zyf.javabasic.spring.failureanalyzer.model; /** * program: zyfboot-javabasic * description: DescriptionAndAction * author: zhangyanfeng * create: 2024-05-02 20:19 **/ public class DescriptionAndAction { private final String description; private final String action; public DescriptionAndAction(String description, String action) { this.description description; this.action action; } public String getDescription() { return description; } public String getAction() { return action; } }然后定义基本的异常信息如下package org.zyf.javabasic.spring.failureanalyzer.exception; import com.alibaba.fastjson.JSON; import org.zyf.javabasic.spring.failureanalyzer.model.ConfigFileFormatErrorInfo; /** * program: zyfboot-javabasic * description: 配置文件格式问题异常 * author: zhangyanfeng * create: 2024-05-02 19:23 **/ public class ConfigFileFormatException extends RuntimeException { private final ConfigFileFormatErrorInfo errorInfo; public ConfigFileFormatException(ConfigFileFormatErrorInfo errorInfo) { super(Configuration file format error: JSON.toJSONString(errorInfo)); this.errorInfo errorInfo; } public ConfigFileFormatErrorInfo getErrorInfo() { return errorInfo; } }检查指定的配置文件report-config.xml是否符合预期的格式要求必须包含一个名为 dataSource 的 Bean 定义用于配置数据库连接信息。dataSource Bean 中必须包含以下属性driverClassName数据库驱动类名url数据库连接 URLusername数据库用户名password数据库密码。password属性必须已加密即以特定字符串开头。必须包含一个名为 reportGenerator 的 Bean 定义用于配置报告生成器。reportGenerator Bean 中必须包含一个名为dataSource的属性引用指向之前定义的 dataSource Bean。如果配置文件不符合上述要求之一就会抛出相应的异常指示配置文件格式错误。具体对应的检查实现如下package org.zyf.javabasic.spring.failureanalyzer.checker; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Component; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.zyf.javabasic.spring.failureanalyzer.exception.ConfigFileFormatException; import org.zyf.javabasic.spring.failureanalyzer.model.ConfigFileFormatErrorInfo; import javax.annotation.PostConstruct; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.InputStream; import static org.zyf.javabasic.spring.failureanalyzer.model.ConfigFileFormatErrorInfo.ErrorType.*; /** * program: zyfboot-javabasic * description: 指定配置文件验证逻辑 * author: zhangyanfeng * create: 2024-05-02 20:12 **/ Component public class ConfigFileFormatChecker { Autowired private ResourceLoader resourceLoader; PostConstruct public void checkConfigFileFormat() { String fileName report-config.xml; Resource resource resourceLoader.getResource(classpath: fileName); if (!resource.exists()) { throw new ConfigFileFormatException(new ConfigFileFormatErrorInfo(true, null, fileName)); } Element root null; try (InputStream inputStream resource.getInputStream()) { DocumentBuilderFactory factory DocumentBuilderFactory.newInstance(); DocumentBuilder builder factory.newDocumentBuilder(); Document document builder.parse(new InputSource(inputStream)); // 获取根元素 root document.getDocumentElement(); } catch (Exception e) { throw new ConfigFileFormatException(new ConfigFileFormatErrorInfo(true, OTHER, fileName)); } // 检查 dataSource Bean 定义 checkDataSourceDefinition(root, fileName); // 检查报告生成器定义 checkReportGeneratorDefinition(root, fileName); } private void checkDataSourceDefinition(Element root, String fileName) { // 获取 dataSource 元素 NodeList dataSourceList root.getElementsByTagName(bean); for (int i 0; i dataSourceList.getLength(); i) { Element dataSourceElement (Element) dataSourceList.item(i); String id dataSourceElement.getAttribute(id); if (dataSource.equals(id)) { // 获取 driverClassName 属性 String driverClassName dataSourceElement.getElementsByTagName(property) .item(0) .getAttributes() .getNamedItem(value) .getNodeValue(); // 获取 url 属性 String url dataSourceElement.getElementsByTagName(property) .item(1) .getAttributes() .getNamedItem(value) .getNodeValue(); // 获取 username 属性 String username dataSourceElement.getElementsByTagName(property) .item(2) .getAttributes() .getNamedItem(value) .getNodeValue(); // 获取 password 属性 String password dataSourceElement.getElementsByTagName(property) .item(3) .getAttributes() .getNamedItem(value) .getNodeValue(); if (StringUtils.isAnyBlank(driverClassName, url, username, password)) { throw new ConfigFileFormatException(new ConfigFileFormatErrorInfo(false, MISSING_PROPERTY, fileName)); } if (!isPasswordEncrypted(password)) { throw new ConfigFileFormatException(new ConfigFileFormatErrorInfo(false, INVALID_VALUE, fileName)); } } } } private void checkReportGeneratorDefinition(Element root, String fileName) { // 获取 reportGenerator 元素 NodeList reportGeneratorList root.getElementsByTagName(bean); for (int i 0; i reportGeneratorList.getLength(); i) { Element reportGeneratorElement (Element) reportGeneratorList.item(i); String id reportGeneratorElement.getAttribute(id); if (reportGenerator.equals(id)) { // 获取 dataSource 属性的引用 String dataSourceRef reportGeneratorElement.getElementsByTagName(property) .item(0) .getAttributes() .getNamedItem(ref) .getNodeValue(); if (StringUtils.isAnyBlank(dataSourceRef)) { throw new ConfigFileFormatException(new ConfigFileFormatErrorInfo(false, MISSING_PROPERTY, fileName)); } } } } private boolean isPasswordEncrypted(String password) { // 检查密码是否已加密这里可以根据具体加密方式进行验证 return password.startsWith(Zyf); } }接着创建并实现AbstractFailureAnalyzer重写analyze()方法如下package org.zyf.javabasic.spring.failureanalyzer.analyzer; import org.springframework.boot.diagnostics.AbstractFailureAnalyzer; import org.springframework.boot.diagnostics.FailureAnalysis; import org.zyf.javabasic.spring.failureanalyzer.exception.ConfigFileFormatException; import org.zyf.javabasic.spring.failureanalyzer.model.ConfigFileFormatErrorInfo; import org.zyf.javabasic.spring.failureanalyzer.model.DescriptionAndAction; /** * program: zyfboot-javabasic * description: 指定配置文件具体格式要求 * author: zhangyanfeng * create: 2024-05-02 20:31 **/ public class ZYFConfigFileFormatFailureanalyzer extends AbstractFailureAnalyzerConfigFileFormatException { Override protected FailureAnalysis analyze(Throwable rootFailure, ConfigFileFormatException cause) { ConfigFileFormatErrorInfo errorInfo cause.getErrorInfo(); String description; String action; if (errorInfo.isFileNotFound()) { description Configuration file errorInfo.getFileName() not found; action Check if the configuration file exists.; } else { DescriptionAndAction descriptionAndAction errorInfo.getDescriptionAndAction(); description descriptionAndAction.getDescription(); action descriptionAndAction.getAction(); } return new FailureAnalysis(description, action, cause); } }spring.factories中增加本次新增验证org.springframework.boot.diagnostics.FailureAnalyzer\ org.zyf.javabasic.spring.failureanalyzer.analyzer.ZYFConfigFileFailureAnalyzer,\ org.zyf.javabasic.spring.failureanalyzer.analyzer.ZYFConfigFileFormatFailureanalyzer但是我实际report-config.xml配置如下?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd !-- 数据库连接信息 -- bean iddataSource classorg.springframework.jdbc.datasource.DriverManagerDataSource property namedriverClassName valuecom.mysql.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/zyf/ property nameusername valueroot/ property namepassword valueZsyf2014/ /bean !-- 报告生成器 -- bean idreportGenerator classorg.zyf.javabasic.spring.beanFactory.ReportGenerator property namedataSource refdataSource/ !-- 其他配置属性 -- /bean /beans由于数据库加密不正确故程序启动报错如下六、一些建议如果确定了需要创建自定义的FailureAnalyzer时必须有几个注意事项确保FailureAnalyzer能够准确地识别失败的原因避免误导性的诊断帮助更快地找到并解决问题。在诊断中提供尽可能详细的信息包括失败的具体原因、发生失败的位置等。提供清晰的建议或解决方案可以包括修复代码、调整配置或执行其他操作的建议。相关源码依旧在常用的github地址中。七、总结在本文中我们深入探讨了FailureAnalyzer这一强大的工具它不仅能帮助我们快速识别和处理代码中的错误还能极大地提升我们的开发效率。通过详细的实例分析我们了解了FailureAnalyzer如何通过自定义逻辑应对不同类型的异常让程序员能够更好地定位问题并迅速找到解决方案。借助FailureAnalyzer我们可以在面对复杂的错误时不再手忙脚乱而是能够从容应对。这种能力的提升不仅让我们的代码更加稳健也让我们在开发过程中保持了更高的生产力和信心。正如我们在引言中提到的面对那些不断“汪汪”作响的错误FailureAnalyzer就像一位贴心的朋友时刻陪伴在侧为我们指引方向。最后掌握FailureAnalyzer的使用不仅能帮助我们更有效地处理错误还能促使我们在编程的旅程中不断成长。希望本文能激励你在未来的项目中充分利用这一工具持续提升自己的代码质量和开发技能。让我们一起迈向更加稳定和高效的编程世界

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价