@ExceptionHandler 原理浅析

前情提要

要从头撸一个项目,想要项目中出现Exception时不会把堆栈信息返给前端;搜索后发现了Spring的@ExceptionHandler注解可以满足这个功能。他注在方法上即可在出现异常时使用这个方法来生成一个相对正常的返回值给前端,用法如下:

@RestControllerAdvice
public class ErrorHandler {
//当代码任意位置抛出MyException的错误时,就会使用这个方法来处理并生成一个相对对正常的返回值
    @ExceptionHandler(value = MyException.class)
    public String myExceptionHandler(MyException e){
        log.error("fatal exception", e);
        return "异常被拦截,服务器内部错误";
    }
}

源码实现

其实在了解到这个注解前是想用AOP来对所有controller做切面的,看到这个注解之后第一感觉也是spring用AOP帮我们实现了这个操作,但是断点调试之后其实不然。

spring对于controller返回的数据是有一个处理流程的,而我们自定义的异常处理方法会被spring嵌入这个处理流程中,下面是调试细节。

image-20220709231113348

这是发生异常时的调用堆栈,借此整理一下spring对于进来请求的处理方式;

当一个请求进来

image-20220709230820683

借用一张网上的spring原理图,如图spring会注册一个DispatchServlet作为处理所有请求的servlet, 根据java web的知识我们可以知道请求进来时会执行servlet中的doService方法。

所以这里暂时只需关心doService以上的调用栈。

doService

首先是判断请求是否是include(包含与转发中的包含),若是 则对参数进行缓存,原文注释是

Keep a snapshot of the request attributes in case of an include,to be able to restore the original attributes after the include.

紧接着在request中加入一些属性,用途是“Make framework objects available to handlers and view objects.”。其中就有WebApplicationContext这个东东

之后才是重头戏,调用了doDispatch来找寻合适的controller来进行处理

image-20220709233900857

doDispatch

这个方法刚开始便准备了一个空的ModelAndView对象和一个Exception对象来承载我们的controller所返回的数据或是抛出的异常。--也就是说我们controller中返回的东西一定要经过Spring处理之后才会响应出去。

准备好这两个空对象之后的代码均在try 块中,如果catch到了异常就会赋给准备好的Exception对象。

之后方法会在事先缓存的List<HandlerAdapter> handlerAdapters中找到处理该url的handlerAdapter对该request进行处理

image-20220710005803886

当一个异常出现

书接上回,在那句ha.handle(processedRequest,...)中,我们的controller如果编写不好就会throw一个异常出来,被上面说的 spring准备好的Exception对象接住,开始进行异常处理。

也就是方法栈中的

processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

看名字可以看出来,这个方法的职责就是格式化一个合理的result作为响应,所以处理Exception也是他的职责。

processHandlerException

processDispatchResult如果发现传入的Exception不为null,就会把Exception与产生错误的handler一同传入这个方法进行处理

这里其实也是一个类似dispatch的过程,只不过是根据Exception的类型来查找handler而不是url。

我们添加过ExceptionHandler注解的方法同样会在项目启动时被缓存在DispatchServlet中如图

image-20220710011031462

也就是说我们自己编写的ExceptionHandler逻辑其实就被镶嵌在了这里,如果没有我们的错误处理器,Spring自己也会有一个默认的处理器来对错误进行基本的处理,所以出了Exception才不会崩嘛~

后记

对于handler如何对应到我们编写的方法,认知还不够清楚,回头有空一定补上;

and 觉得其实在这里学到的最有价值的是用断点来调程序读源码的方法论,之前几乎没有用断点来调程序的意识,这次试了试属实是好用!以后多用~

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇