2020 以前打算将 Spring 和 SpringBoot 的官方文档看一遍,顺便看点感兴趣的源码。
昨天莫名想研究在 SpringBoot 中 Exception 怎么处理的。
复制代码
代码 GitHubjava
实现这个接口 ApplicationListener<ApplicationFailedEvent> ,
将 Bean 加入 ioc ,当程序启动发生异常你会感知到。好比启动失败发送邮件通知。
实现这个接口 ApplicationListener<ApplicationReadyEvent> ,
将 Bean 加入 ioc 容器中,当程序启动成功你会感知到。
基于实现 SpringBootExceptionReporter,对启动异常分析,
在咱们自定义 starter 颇有用
复制代码
@ExceptionHandler 和 @RestControllerAdvice 结合。处理标记的异常。
Tomcat 会根据 Response 判断是否有异常须要处理。
而后转发 DispatcherServlet url /error,这个可路径在 SpringBoot 可修改。
BasicErrorController 既是处理 /error 。
会讲一些源码,记录一下处理流程。
复制代码
看源码的时候学到的,很强大的功能。ResolvableType
复制代码
SpringApplication.run(String... args) 中启动系统上下文,当发生异常的时候,
SpringApplication.handleRunFailure 处理启动异常逻辑。
一、会发送失败事件,可经过监听事件,处理逻辑。
二、SpringApplication.reportFailure 分析平常信息。
实现这个接口 SpringBootExceptionReporter 就能够注册异常了。
不过 SpringBoot FailureAnalyzers 给了默认实现。
咱们能够基于 FailureAnalyzers 的逻辑进行扩展。
复制代码
经过 ApplicationEvent 事件,及发布事件,能够很好的解耦。git
咱们也能够经过自定义业务事件进行结构业务。github
监听启动失败的事件spring
@Component
public class StartFailedApplicationListener implements ApplicationListener<ApplicationFailedEvent> {
@Autowired
private EmailService emailService;
@Override
public void onApplicationEvent(ApplicationFailedEvent applicationFailedEvent) {
// 发送邮件告诉启动失败了
Throwable exception = applicationFailedEvent.getException();
// 31 红色 32 绿色 33 黄色
StringJoiner stringJoiner = new StringJoiner("", "\031[32;4m", "\031[0m");
String join = String.join("", "服务器 ip: 192.168.11.11 启动失败, 异常缘由为:", exception.getMessage());
String s = stringJoiner.add(join).toString();
emailService.sendEmail(s);
}
}
复制代码
实现接口监听启动成功的事件apache
@Component
public class StartSuccessApplicationListener implements ApplicationListener<ApplicationReadyEvent> {
@Autowired
private EmailService emailService;
@Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
// 发送邮件告诉启动成功了
// 31 红色 32 绿色 33 黄色
StringJoiner stringJoiner = new StringJoiner("", "\033[32;4m", "\033[0m");
String join = String.join("", "服务器 ip: 192.168.11.11 启动成功!");
String s = stringJoiner.add(join).toString();
emailService.sendEmail(s);
}
}
复制代码
使用注解监听事件api
@Component
public class AnnotationListener {
@EventListener(value={ApplicationReadyEvent.class})
public void annotationListener(){
System.out.println(AnnotationListener.class.getName()+"启动成功了");
}
}
复制代码
第一步继承 AbstractFailureAnalyzer,确认处理那个异常。springboot
public class StartExceptionFailureAnalyzer extends AbstractFailureAnalyzer<RuntimeException> {
@Override
protected FailureAnalysis analyze(Throwable rootFailure, RuntimeException cause) {
Throwable rootCause = cause.getCause();
if (rootCause instanceof StartException) {
return new FailureAnalysis("测试启动异常","",rootCause);
}
return null;
}
}
复制代码
第二步实现 FailureAnalysisReporter,确认处理某个异常的逻辑。服务器
public class MyFailureAnalysisReporter implements FailureAnalysisReporter {
private EmailService emailService;
public MyFailureAnalysisReporter(){
emailService=new EmailService();
}
@Override
public void report(FailureAnalysis analysis) {
final Throwable cause = analysis.getCause();
final String message = cause.getMessage();
emailService.sendEmail(String.join("","异常缘由:",message));
}
}
复制代码
第三部将上述两个类加入到 spring.factoriesrestful
SpringFactoriesLoader 能够加载 spring.factories 的类app
org.springframework.boot.diagnostics.FailureAnalyzer=\
com.fly.exception.start.analyzer.StartExceptionFailureAnalyzer
org.springframework.boot.diagnostics.FailureAnalysisReporter=\
com.fly.exception.start.analyzer.MyFailureAnalysisReporter
复制代码
建议返回值能够设置成 ResponseEntity,比较容易设置请求头和状态码,restful 接口实现的时候挺有用。
@Component
@RestControllerAdvice
public class HandleActionException extends ResponseEntityExceptionHandler {
public HandleActionException(){
}
@Override
protected ResponseEntity handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
request.setAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE, ex, WebRequest.SCOPE_REQUEST);
}
final ResponseEntity<RetUtil> retUtilResponseEntity = new ResponseEntity<>(RetUtil.build().code(5000), headers, status);
return retUtilResponseEntity;
}
@ExceptionHandler(value = {RuntimeException.class})
public ResponseEntity<RetUtil> handleRunTimeException(){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(RetUtil.build().code(5000));
}
}
复制代码
代码大体处理逻辑以下
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class MyErrorController extends BasicErrorController {
private ApplicationContext applicationContext;
public MyErrorController(ErrorAttributes errorAttributes, ServerProperties serverProperties, List<ErrorViewResolver> errorViewResolvers) {
super( errorAttributes, serverProperties.getError(), errorViewResolvers);
}
@Override
@RequestMapping
public ResponseEntity error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return ResponseEntity.status(status).body(RetUtil.build().code(status.value()));
}
Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
return new ResponseEntity<>(RetUtil.build().code(status.value()).data(body), status);
}
}
复制代码
Response.sendError 会将设置 org.apache.coyote.Response 对应的状态码 和 errorState,errorState 为 1 说明有问题。Tomcat 会转发 /error
判断 errorState 是否为 1,为 1 进行业务处理,转发 /error
转发请求,转发 /error 不会 执行过滤器了。
上述图片执行过滤器链逻辑
上述图片,责任链模式执行过滤器链(不会执行过滤器的操做),而后执行 DispatcherServlet.doDispatch
当使用通配符,没有使用限定符,是不能获取的。
ResolvableType 描述一个 Class 的信息
相等于对 Class 的 api 封装了一些东西,很方便使用。
ResolvableType.resolveGenerics 获取当前泛型。
ResolvableType.getInterfaces 获取父接口 Class 信息
ResolvableType.getSuperType 获取父类的 Class 信息
复制代码
@Test
public void run77() {
final MyList55<String, Demo2> stringDemo2MyList55 = new MyList55<>();
final ResolvableType resolvableType = ResolvableType.forInstance(stringDemo2MyList55);
// null
// null
for (ResolvableType generic : resolvableType.getGenerics()) {
System.out.println(generic.resolve());
}
}
复制代码
public interface MyGenericInterface<T extends CharSequence, M extends Demo> {
default void onApplicationEvent1(T event,M event3){
System.out.println(event.charAt(1));
System.out.println(event3.getName());
}
}
public class MyList2 implements MyGenericInterface<String,Demo>{
}
public class MyList33 implements MyGenericInterface<String,Demo2> {
}
@Data
public class Demo {
private String name;
}
public class Demo2 extends Demo {
}
复制代码
@Test
public void run22() {
ResolvableType resolvableType = ResolvableType.forClass(MyList2.class);
final ResolvableType[] interfaces = resolvableType.getInterfaces();
final ResolvableType[] generics = interfaces[0].getGenerics();
/** * class java.lang.String * class com.fly.exception.Demo */
for (ResolvableType generic : generics) {
System.out.println(generic.resolve());
}
}
@Test
public void run33() {
ResolvableType resolvableType = ResolvableType.forClass(MyList33.class);
final ResolvableType[] interfaces = resolvableType.getInterfaces();
final ResolvableType[] generics = interfaces[0].getGenerics();
/** * class java.lang.String * class com.fly.exception.Demo2 */
for (ResolvableType generic : generics) {
System.out.println(generic.resolve());
}
}
复制代码
@Test
public void run44() {
final MyList33 myList33 = new MyList33();
final ResolvableType resolvableType = ResolvableType.forInstance(myList33);
/** * class com.fly.exception.MyList33 */
System.out.println(resolvableType.resolve());
final ResolvableType[] interfaces = resolvableType.getInterfaces();
for (ResolvableType anInterface : interfaces) {
final ResolvableType[] generics = anInterface.getGenerics();
/** * class java.lang.String * class com.fly.exception.Demo2 */
for (ResolvableType generic : generics) {
System.out.println(generic.resolve());
}
}
}
复制代码
@Test
public void run55() {
MyList44<String, Demo> objectObjectMyList44 = new MyList44<>();
final ResolvableType resolvableType = ResolvableType.forInstance(objectObjectMyList44);
// class java.lang.String
// class com.fly.exception.Demo
for (ResolvableType generic : resolvableType.getGenerics()) {
System.out.println(generic.resolve());
}
}
复制代码
@Test
public void run66() {
final ResolvableType resolvableType = ResolvableType.forClass(MyList44.class);
// class java.lang.String
// class com.fly.exception.Demo
for (ResolvableType generic : resolvableType.getGenerics()) {
System.out.println(generic.resolve());
}
}
复制代码