FailureAnalyzer
拦截启动时异常,将异常转换成更加易读的信息并包装成FailureAnalysis
对象。Spring Boot 为应用上下文相关异常、JSR-303 validations 提供了此类分析器。
AbstractFailureAnalyzer
是FailureAnalyzer
的抽象实现,检查要处理的异常中是否存在指定的异常类型。扩展AbstractFailureAnalyzer
可以实现自定义异常处理,如果无法处理该异常则可以返回null
,以便其它FailureAnalyzer
有机会处理该异常。
一、自定义 FailureAnalyzer
- 定义一个配置类,使用
@Value
注入一个未定义的属性。
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
@Value("${undefined}")
private String value;
}
- 启动日志打印错误提示
2021-12-06 20:48:27.531 INFO 4088 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 17.0.1 on DESKTOP with PID 4088 (D:\learn\Spring\demo\target\classes started by ... in d:\learn\Spring\demo)
2021-12-06 20:48:27.534 INFO 4088 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2021-12-06 20:48:27.916 WARN 4088 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'config': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'undefined' in value "${undefined}"
2021-12-06 20:48:27.924 INFO 4088 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-12-06 20:48:27.942 ERROR 4088 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'config': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'undefined' in value "${undefined}"
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:405) ~[spring-beans-5.3.13.jar:5.3.13]
AutowiredAnnotationBeanPostProcessor.java:405
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1431) ~[spring-beans-5.3.13.jar:5.3.13]
AbstractAutowireCapableBeanFactory.java:1431
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:619) ~[spring-beans-5.3.13.jar:5.3.13]
AbstractAutowireCapableBeanFactory.java:619
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.13.jar:5.3.13]
AbstractAutowireCapableBeanFactory.java:542
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335) ~[spring-beans-5.3.13.jar:5.3.13]
AbstractBeanFactory.java:335
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.13.jar:5.3.13]
DefaultSingletonBeanRegistry.java:234
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333) ~[spring-beans-5.3.13.jar:5.3.13]
AbstractBeanFactory.java:333
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208) ~[spring-beans-5.3.13.jar:5.3.13]
AbstractBeanFactory.java:208
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944) ~[spring-beans-5.3.13.jar:5.3.13]
DefaultListableBeanFactory.java:944
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918) ~[spring-context-5.3.13.jar:5.3.13]
AbstractApplicationContext.java:918
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583) ~[spring-context-5.3.13.jar:5.3.13]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[spring-boot-2.6.1.jar:2.6.1]
SpringApplication.java:1290
at com.example.demo.DemoApplication.main(DemoApplication.java:11) ~[classes/:na]
DemoApplication.java:11
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'undefined' in value "${undefined}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-5.3.13.jar:5.3.13]
PropertyPlaceholderHelper.java:180
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-5.3.13.jar:5.3.13]
PropertyPlaceholderHelper.java:126
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[spring-core-5.3.13.jar:5.3.13]
AbstractPropertyResolver.java:239
at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210) ~[spring-core-5.3.13.jar:5.3.13]
AbstractPropertyResolver.java:210
at org.springframework.context.support.PropertySourcesPlaceholderConfigurer.lambda$processProperties$0(PropertySourcesPlaceholderConfigurer.java:175) ~[spring-context-5.3.13.jar:5.3.13]
PropertySourcesPlaceholderConfigurer.java:175
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:936) ~[spring-beans-5.3.13.jar:5.3.13]
AbstractBeanFactory.java:936
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1321) ~[spring-beans-5.3.13.jar:5.3.13]
DefaultListableBeanFactory.java:1321
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300) ~[spring-beans-5.3.13.jar:5.3.13]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.resolveFieldValue(AutowiredAnnotationBeanPostProcessor.java:656) ~[spring-beans-5.3.13.jar:5.3.13]
AutowiredAnnotationBeanPostProcessor.java:656
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639) ~[spring-beans-5.3.13.jar:5.3.13]
AutowiredAnnotationBeanPostProcessor.java:639
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.13.jar:5.3.13]
InjectionMetadata.java:119
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.13.jar:5.3.13]
AutowiredAnnotationBeanPostProcessor.java:399
... 16 common frames omitted
- 自定义
FailureAnalyzer
继承AbstractFailureAnalyzer
。
package com.example.demo;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
public class IllegalInjectionFailureAnalyzer extends AbstractFailureAnalyzer<IllegalArgumentException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, IllegalArgumentException cause) {
return new FailureAnalysis(cause.getMessage(), "Please check injection param!", cause);
}
}
- 在
src/main/resource
目录下新建META-INF/spring.factories
,在spring.factories
中添加如下内容,这是注册自定义FailureAnalyzer
的方式。
org.springframework.boot.diagnostics.FailureAnalyzer=com.example.demo.IllegalInjectionFailureAnalyzer
- 再次启动应用,失败日志如下。
2021-12-07 10:50:39.875 INFO 22236 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 17.0.1 on DESKTOP with PID 22236 (D:\learn\Spring\demo\target\classes started by ... in d:\learn\Spring\demo)
2021-12-07 10:50:39.877 INFO 22236 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2021-12-07 10:50:40.241 WARN 22236 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'config': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'undefined' in value "${undefined}"
2021-12-07 10:50:40.250 INFO 22236 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-12-07 10:50:40.270 ERROR 22236 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Could not resolve placeholder 'undefined' in value "${undefined}"
Action:
Please check injection param!
二、自定义多个 FailureAnalyzer
- 在上例基础上新增一个自定义
FailureAnalyzer
类处理空指针异常。
package com.example.demo;
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
import org.springframework.boot.diagnostics.FailureAnalysis;
public class NullPointerFailureAnalyzer extends AbstractFailureAnalyzer<NullPointerException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, NullPointerException cause) {
return new FailureAnalysis(cause.getMessage(), "Please check null pointer!", cause);
}
}
- 修改
src/main/resources/META-INF/spring.factories
,注册NullPointerFailureAnalyzer
。
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.example.demo.IllegalInjectionFailureAnalyzer,\
com.example.demo.NullPointerFailureAnalyzer
- 修改配置类,模拟空指针异常。
package com.example.demo;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Config {
public Config() {
String param = null;
System.out.println("Param: " + param.toString());
}
}
- 启动应用,错误日志如下。
2021-12-07 16:58:54.949 INFO 8716 --- [ main] com.example.demo.DemoApplication : Starting DemoApplication using Java 17.0.1 on DESKTOP with PID 8716 (D:\learn\Spring\demo\target\classes started by ... in d:\learn\Spring\demo)
2021-12-07 16:58:54.951 INFO 8716 --- [ main] com.example.demo.DemoApplication : No active profile set, falling back to default profiles: default
2021-12-07 16:58:55.300 WARN 8716 --- [ main] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'config' defined in file [D:\learn\Spring\demo\target\classes\com\example\demo\Config.class]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.example.demo.Config$$EnhancerBySpringCGLIB$$47133ca1]: Constructor threw exception; nested exception is java.lang.NullPointerException: Cannot invoke "String.toString()" because "param" is null
2021-12-07 16:58:55.310 INFO 8716 --- [ main] ConditionEvaluationReportLoggingListener :
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2021-12-07 16:58:55.326 ERROR 8716 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
Cannot invoke "String.toString()" because "param" is null
Action:
Please check null pointer!
三、注意事项
自定义FailureAnalyzer
对于监控应用的启动过程十分有用,但是自定义FailureAnalyzer
处理不了main
方法中的异常。