SpringBoot exception异常处理机制源码解析
一、Spring Boot默认的异常处理机制
1:浏览器默认返回效果

2:原理解析
为了便于源码跟踪解析,在·Controller中手动设置异常。
@RequestMapping(value="/emps",method=RequestMethod.GET)
public String emps(Map<String,Object> map){ System.out.println("emp list .......");
Collection<Employee> emps=employeeDao.getAll();
map.put("emps", emps);
int i=/;//设置异常 return "list";
}
浏览器访问该请求 http://localhost:8080/crud/emps,后台进入DispatcherServlet。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
第17行获取HandlerExecutionChain,根据url:/emps 从RequestMappingHandlerMapping获取HandlerExecutionChain
第24行根据Handler获取相应的RequestMappingHandlerAdapter
第44行RequestMappingHandlerAdapter调用handle(),其中完成Cnotroller.emps()调用
1)argumentResolvers根据HandlerMethod.parameters 完成emps方法入参的准备
2)执行InvocableHandlerMethod.doInvoke;
3)returnValueHandlers.handleReturnValue对Cnotroller.emps() 返回参数的对应处理
由于在emps()手动设置了异常,上述第2步调用InvocableHandlerMethod.doInvoke时会抛出 java.lang.ArithmeticException: / by zero
程序执行进入54行,然后执行61行:processDispatchResult。


1222行由HandlerExceptionResolver对异常进行处理.其中DefaultErrorAttributes的exception处理:

最后1244 行抛出异常,然后一层一层往外抛出此异常,直到最外层StandardWrapperValve.invoke()对此异常进行了处理


然后程序执行到StandardHostValve的173行,判断是否需要进行response的异常处理

StandardHostValve中status()中,229行会从容器中配置的ErrorPage:转发后默认处理的ErrorController

StandardHostValve.custom()中 根据errorPage.location进行转发。

SpringBoot中BasicErrorController:处理默认异常转发的/error请求
@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
@RequestMapping(produces = "text/html") //产生html类型的数据;浏览器发送的请求来到这个方法处理
public ModelAndView errorHtml(HttpServletRequest request,
HttpServletResponse response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes(
request, isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
ModelAndView modelAndView = resolveErrorView(request, response, status, model);//去哪个页面作为错误页面;包含页面地址和页面内容
return (modelAndView != null) ? modelAndView : new ModelAndView("error", model);
} @RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {//产生json数据,其他客户端来到这个方法处理;
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
HttpStatus status = getStatus(request);
return new ResponseEntity<Map<String, Object>>(body, status);
}
DefaultErrorAttributes中设置页面的共享信息
@Override
public Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes,
boolean includeStackTrace) {
Map<String, Object> errorAttributes = new LinkedHashMap<String, Object>();
errorAttributes.put("timestamp", new Date());
addStatus(errorAttributes, requestAttributes);
addErrorDetails(errorAttributes, requestAttributes, includeStackTrace);
addPath(errorAttributes, requestAttributes);
return errorAttributes;
}
DefaultErrorViewResolver
@Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,
Map<String, Object> model) {
ModelAndView modelAndView = resolve(String.valueOf(status), model);
if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) {
modelAndView = resolve(SERIES_VIEWS.get(status.series()), model);
}
return modelAndView;
} private ModelAndView resolve(String viewName, Map<String, Object> model) {
String errorViewName = "error/" + viewName;//默认SpringBoot可以去找到一个页面? error/500
TemplateAvailabilityProvider provider = this.templateAvailabilityProviders
.getProvider(errorViewName, this.applicationContext);//模板引擎可以解析这个页面地址就用模板引擎解析
if (provider != null) {
return new ModelAndView(errorViewName, model);//模板引擎可用的情况下返回到errorViewName指定的视图地址
}
return resolveResource(errorViewName, model);//模板引擎不可用,就在静态资源文件夹下找errorViewName对应的页面 error/500.html
}
SpringBoot exception异常处理机制源码解析的更多相关文章
- SpringBoot的条件注解源码解析
SpringBoot的条件注解源码解析 @ConditionalOnBean.@ConditionalOnMissingBean 启动项目 会在ConfigurationClassBeanDefini ...
- SpringBoot 2.0.3 源码解析
前言 用SpringBoot也有很长一段时间了,一直是底层使用者,没有研究过其到底是怎么运行的,借此机会今天试着将源码读一下,在此记录...我这里使用的SpringBoot 版本是 2.0.3.RE ...
- 史上最详细的Android消息机制源码解析
本人只是Android菜鸡一个,写技术文章只是为了总结自己最近学习到的知识,从来不敢为人师,如果里面有不正确的地方请大家尽情指出,谢谢! 606页Android最新面试题含答案,有兴趣可以点击获取. ...
- Android Handler消息机制源码解析
好记性不如烂笔头,今天来分析一下Handler的源码实现 Handler机制是Android系统的基础,是多线程之间切换的基础.下面我们分析一下Handler的源码实现. Handler消息机制有4个 ...
- Spring事件监听机制源码解析
Spring事件监听器使用 1.Spring事件监听体系包括三个组件:事件.事件监听器,事件广播器. 事件:定义事件类型和事件源,需要继承ApplicationEvent. package com.y ...
- Eureka自我保护机制源码解析
默认情况下,当EurekaServer在一定时间内(默认90秒)没有接收到某个客户端实例的心跳,EurekaServer将会注销该实例.但是当网络分区故障发生时,客户端与EurekaServer之间无 ...
- Android View 事件分发机制 源码解析 (上)
一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...
- SpringBoot自动配置的源码解析
首先,写源码分析真的很花时间,所以希望大家转的时候也请注明一下,Thanks♪(・ω・)ノ SpringBoot最大的好处就是对于很多框架都默认的配置,让我们开发的时候不必为了大一堆的配置文件头疼,关 ...
- 【spring源码学习】spring的事件发布监听机制源码解析
[一]相关源代码类 (1)spring的事件发布监听机制的核心管理类:org.springframework.context.event.SimpleApplicationEventMulticast ...
随机推荐
- 20175313 张黎仙《获奖感想与Java阶段性学习总结》
一.获奖感想 很荣幸能够成为为数不多的小黄衫获得者之一,这是对我近一学期以来学习成果的肯定,也激励着我更加努力学习. 首先我要感谢的人就是娄嘉鹏老师.我曾在师生关系中提到,我认为的好老师的特点之一是: ...
- Android 系统添加SELinux权限
本文为博主原创文章,转载请注明出处:https://i.cnblogs.com/EditPosts.aspx?postid=11185476 CPU:RK3288 系统:Android 5.1 SEL ...
- Java 面向对象(十三)
异常 什么是异常 异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止. 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象. ...
- usb 设备 复位
How to Reset USB Device in Linux by ROMAN10 on MAY 4, 2011 · 9 COMMENTS USB devices are anywhere n ...
- GoogleNet-ILSVRC-2014冠军
Going deeper with convolutions-22层 https://my.oschina.net/u/876354/blog/1637819 那么,GoogLeNet是如何进一步提升 ...
- 002-创建型-05-原型模式(Protype)
一.概述 指原型实例指定创建对象的种类,并通过克隆这些原型创建新的对象 原型模式就是让类实现Cloneable接口,达到克隆原型类的方式. 1.1.适用场景 1.在创建对象的时候,我们不只是希望被创建 ...
- SortedMap和TreeMap有什么区别?
SortedMap和TreeMap有什么区别 答: TreeMap的类的源码: public class TreeMap<K,V> extends AbstractMap<K,V ...
- kubenetes安装
master节点主要由四个模块组成: APIServer 提供了资源操作的唯一入口,任何对资源进行增删改查的操作都要交给APIServer处理后再提交给etcd.kubectl就是对api ser ...
- 【Leetcode_easy】653. Two Sum IV - Input is a BST
problem 653. Two Sum IV - Input is a BST 参考 1. Leetcode_easy_653. Two Sum IV - Input is a BST; 完
- 三节课MINI计划第五周
一.任务及干货 二.作品 (一)小组分工 (二)社群运营方案