本文介绍 Spring Boot 2 自定义 FailureAnalyzer
的方法。
目录
-
FailureAnalyzer
简介 - 开发环境
- 基础示例
- 自定义多个
FailureAnalyzer
示例 - 总结
FailureAnalyzer
简介
org.springframework.boot.diagnostics.FailureAnalyzer
拦截启动时异常,将异常转换成更加易读的信息并包装成 org.springframework.boot.diagnostics.FailureAnalysis
对象。Spring Boot 为应用上下文相关异常、JSR-303 validations
提供了此类分析器,也可以自定义 FailureAnalyzer
。
org.springframework.boot.diagnostics.AbstractFailureAnalyzer
是 FailureAnalyzer
的抽象实现,检查要处理的异常中是否存在指定的异常类型。扩展 AbstractFailureAnalyzer
可以实现自定义异常处理,如无法处理该异常则可以返回 null
,以便其它 FailureAnalyzer
有机会处理该异常。
开发环境
- Oracle JDK 11.0.6
- Apache Maven 3.6.3
- IntelliJ IDEA (Version 2019.3.2)
基础示例
创建 Spring Boot 工程,参考:IntelliJ IDEA 创建 Spring Boot 工程。
生成的
pom
文件如下,不做任何修改
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
tutorial.spring.boot
spring-boot-failure-analyzer
0.0.1-SNAPSHOT
spring-boot-failure-analyzer
自定义FailureAnalyzer
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
- 不添加任何配置(不修改 application.properties 或 application.yml 文件),定义一个配置类,使用
@Value
注解注入一个未定义的属性。
package tutorial.spring.boot.failure.analyzer.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class InitialConfig {
@Value("${application.name}")
private String applicationName;
}
- 启动应用,日志打印出以下错误
2019-04-20 13:37:13.245 INFO 3920 --- [ main] f.a.SpringBootFailureAnalyzerApplication : Starting SpringBootFailureAnalyzerApplication on ... with PID 3920 (D:\Tutorial\spring-boot-tutorial\spring-boot-failure-analyzer\target\classes started by ... in D:\Tutorial\spring-boot-tutorial)
2019-04-20 13:37:13.248 INFO 3920 --- [ main] f.a.SpringBootFailureAnalyzerApplication : No active profile set, falling back to default profiles: default
2019-04-20 13:37:13.742 WARN 3920 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initialConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'application.name' in value "${application.name}"
2019-04-20 13:37:13.750 INFO 3920 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-04-20 13:37:13.765 ERROR 3920 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initialConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'application.name' in value "${application.name}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:380) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1411) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:592) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) [spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE]
at tutorial.spring.boot.failure.analyzer.SpringBootFailureAnalyzerApplication.main(SpringBootFailureAnalyzerApplication.java:10) [classes/:na]
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'application.name' in value "${application.name}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:172) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:124) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:237) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:211) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:851) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1188) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1167) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:593) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:90) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:374) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
... 16 common frames omitted
- 自定义
FailureAnalyzer
,处理java.lang.IllegalArgumentException
异常。
5.1 首先,创建自定义 FailureAnalyzer
类,继承 AbstractFailureAnalyzer
。
package tutorial.spring.boot.failure.analyzer.custom;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
public class IllegalArgumentFailureAnalyzer extends AbstractFailureAnalyzer {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, IllegalArgumentException cause) {
return new FailureAnalysis(cause.getMessage(), "Please check startup argument!", cause);
}
}
5.2 其次,在 src/main/resources
路径下新建 META-INF/spring.factories
,在 spring.factories
文件中添加一下内容。
org.springframework.boot.diagnostics.FailureAnalyzer=tutorial.spring.boot.failure.analyzer.custom.IllegalArgumentFailureAnalyzer
这一步非常重要,自定义的 FailureAnalyzer
实现必须通过这种方式进行注册!
- 再次运行应用,启动日志如下。
2019-04-20 13:43:52.099 INFO 4708 --- [ main] f.a.SpringBootFailureAnalyzerApplication : Starting SpringBootFailureAnalyzerApplication on ... with PID 4708 (D:\Tutorial\spring-boot-tutorial\spring-boot-failure-analyzer\target\classes started by ... in D:\Tutorial\spring-boot-tutorial)
2019-04-20 13:43:52.102 INFO 4708 --- [ main] f.a.SpringBootFailureAnalyzerApplication : No active profile set, falling back to default profiles: default
2019-04-20 13:43:52.631 WARN 4708 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initialConfig': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'application.name' in value "${application.name}"
2019-04-20 13:43:52.639 INFO 4708 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-04-20 13:43:52.641 ERROR 4708 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Could not resolve placeholder 'application.name' in value "${application.name}"
Action:
Please check startup argument!
自定义多个 FailureAnalyzer
示例
- 在以上示例基础上新增一个
NullPointerFailureAnalyzer
,处理java.lang.NullPointerException
异常。
package tutorial.spring.boot.failure.analyzer.custom;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
public class NullPointerFailureAnalyzer extends AbstractFailureAnalyzer {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, NullPointerException cause) {
return new FailureAnalysis(cause.getMessage(), "Please check null pointer!", cause);
}
}
- 修改
InitialConfig
,创造一个NullPointerException
出现场景。
package tutorial.spring.boot.failure.analyzer.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class InitialConfig {
public InitialConfig() {
String param = null;
System.out.println("Param: " + param.toString());
}
}
- 修改
src/main/resources/META-INF/spring.factories
,注册NullPointerFailureAnalyzer
。
org.springframework.boot.diagnostics.FailureAnalyzer=\
tutorial.spring.boot.failure.analyzer.custom.IllegalArgumentFailureAnalyzer,\
tutorial.spring.boot.failure.analyzer.custom.NullPointerFailureAnalyzer
- 运行应用,启动日志如下。
2019-04-20 13:55:40.124 INFO 11564 --- [ main] f.a.SpringBootFailureAnalyzerApplication : Starting SpringBootFailureAnalyzerApplication on ... with PID 11564 (D:\Tutorial\spring-boot-tutorial\spring-boot-failure-analyzer\target\classes started by ... in D:\Tutorial\spring-boot-tutorial)
2019-04-20 13:55:40.129 INFO 11564 --- [ main] f.a.SpringBootFailureAnalyzerApplication : No active profile set, falling back to default profiles: default
2019-04-20 13:55:40.684 WARN 11564 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'initialConfig' defined in file [D:\Tutorial\spring-boot-tutorial\spring-boot-failure-analyzer\target\classes\tutorial\spring\boot\failure\analyzer\config\InitialConfig.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [tutorial.spring.boot.failure.analyzer.config.InitialConfig$$EnhancerBySpringCGLIB$$6a84093d]: Constructor threw exception; nested exception is java.lang.NullPointerException
2019-04-20 13:55:40.693 INFO 11564 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2019-04-20 13:55:40.694 ERROR 11564 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
null
Action:
Please check null pointer!
总结
自定义 FailureAnalyzer
对于监控应用启动过程十分有用。
注意:自定义 FailureAnalyzer
处理不了应用启动 main
方法中的异常,可以将以上示例中 NullPointerException
出现场景放在启动的 main
方法中,如下所示。
package tutorial.spring.boot.failure.analyzer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootFailureAnalyzerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootFailureAnalyzerApplication.class, args);
String param = null;
System.out.println("Param: " + param.toString());
}
}
启动日志如下。
2019-04-20 13:58:55.885 INFO 5208 --- [ main] f.a.SpringBootFailureAnalyzerApplication : Starting SpringBootFailureAnalyzerApplication on ... with PID 5208 (D:\Tutorial\spring-boot-tutorial\spring-boot-failure-analyzer\target\classes started by ... in D:\Tutorial\spring-boot-tutorial)
2019-04-20 13:58:55.889 INFO 5208 --- [ main] f.a.SpringBootFailureAnalyzerApplication : No active profile set, falling back to default profiles: default
Exception in thread "main" java.lang.NullPointerException
at tutorial.spring.boot.failure.analyzer.SpringBootFailureAnalyzerApplication.main(SpringBootFailureAnalyzerApplication.java:12)
2019-04-20 13:58:56.562 INFO 5208 --- [ main] f.a.SpringBootFailureAnalyzerApplication : Started SpringBootFailureAnalyzerApplication in 1.07 seconds (JVM running for 1.802)
源码:https://gitee.com/jyl1626938665/spring-tutorial/tree/master/spring-boot-tutorial/spring-boot-failure-analyzer