springmvc视图解析器原理 (springmvc源码深度解析)

关注回复java 1024G最新java学习资料(包含大数据资料)免费领;

异常处理重要性

  1. 良好的异常处理体系,便于对程序的后期维护
  2. 当发生错误时,程序不至于崩溃,提高程序健壮性
  3. 当发生错误时,可以在短时间内找定位问题所在
  4. 当发生错误时,避免异常栈裸奔,暴露底层架构,提高项目安全性

Spring统一异常方式

  • 使用 @ ExceptionHandler 注解(缺点:异常处理的方法必须与出错的方法在同一个Controller里面,不能全局处理)
1 // 需要捕捉的异常
2 @ExceptionHandler({ BizException.class })
3 // Value用于设置response的状态码,例如404,200等,reason用于响应,可以是内容语句。
4 @ResponseStatus(code=HttpStatus.BAD_REQUEST,reason="bad request")
5 // 可以返回Json也可以进行跳转
6 @ResponseBody
7 public ServerResponse<?> exception(BizException e) {
8 return ServerResponse.createByErrorMessage(e.getMessage());
9 }
  • 实现 HandlerExceptionResolver 接口
11 @Component 
2 public class GlobalExceptionResolver implements HandlerExceptionResolver{ 
3
4 public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { 
5 //Object handler ----> HandlerMethod 也可能为null
6 // 可以通过handler进行特定处理
7 ..............
8 } 
9} 

springmvc校验框架,springmvc源代码解析

  • 使用@ControllerAdvice+ @ ExceptionHandler注解
 1@ControllerAdvice
 2@ResponseBody
 3public class GlobalExceptionResolver {
 4 @ResponseStatus(HttpStatus.BAD_REQUEST)
 5 // 可以不指定特定的异常即默认拦截所有异常
 6 @ExceptionHandler(HttpMessageNotReadableException.class)
 7 public ServiceResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
 8 return ServerResponse.createByErrorMessage(e.getMessage())
 9 }
10 // 其他代码省略
11}

SpringMVC异常处理源码剖析

  • 从DispatcherServlet的doDispatch方法入手
 1protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
 2 try {
 3 //省略请求处理代码部分
 4 }catch (Exception ex) {
 5 dispatchException = ex;
 6 }
 7 // 捕捉异常后调用processDispatchResult方法
 8 processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
 9 }
10 finally {
11
12 }
13 }
  • processDispatchResult如何处理呢
 1private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
 2 HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
 3 // 先判断异常是否为null 
 4 if (exception != null) {
 5 if (exception instanceof ModelAndViewDefiningException) {
 6 logger.debug("ModelAndViewDefiningException encountered", exception);
 7 mv = ((ModelAndViewDefiningException) exception).getModelAndView();
 8 }else {
 9 Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
10 mv = processHandlerException(request, response, handler, exception);
11 errorView = (mv != null);
12 }
13 }
14 }
  • processHandlerException核心代码
 1protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
 2 Object handler, Exception ex) throws Exception {
 3 // 省略了不关系部分(异常解析器排序)
 4 ModelAndView exMv = null;
 5 for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
 6 // 调用resolveException方法
 7 // Spring 自带的异常处理器暂时不讲本质上和我们自定义差不多
 8 exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
 9 if (exMv != null) {
10 // 只要获取ModeAndView终止处理 
11 break;
12 }
13 }
14 // 如果没获得直接抛出异常
15 throw ex;
16 }
  • Spring自带异常解析器主要接口和实现类

springmvc校验框架,springmvc源代码解析

  • AbstractHandlerMethodExceptionResolver和ExceptionHandlerExceptionResolver负责解析@ExceptionHandle
  • ResponseStatusExceptionResolver解析@ResponseStatus
  • DefaultHandlerExceptionResolver按照不同的类型分别对异常进行解析
  • SimpleMappingExceptionResolver: 通过配置的异常类和view的对应关系来解析异常

拦截404

上面介绍的方法并不能拦截404,为什么要拦截404呢?首先为了产品的安全,不随便暴露后台所用的中间件,避免黑客利用中间件本身的漏洞攻击网站,另外也可以项目与用户有良好的交互。

  • 利用Spring MVC的最精确匹配原则(@requestMapping("*)拦截的这个方法返回一个自定义的404界面)
  • 利用web容器提供的error-page
1<error-page>
2 // 也可以拦截其他错误码比如500
3 <error-code>404</error-code>
4 // 确保resource目录不被spring拦截
5 <location>/resource/view/404.htm</location>
6 </error-page>
  • 重写DispatcherServlet的noHandlerFound方法
 1protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception{
 2 if(pageNotFoundLogger.isWarnEnabled())
 3 pageNotFoundLogger.warn((new StringBuilder()).append("No mapping found for HTTP request with URI [").append(getRequestUri(request)).append("] in DispatcherServlet with name ’").append(getServletName()).append("’").toString());
 4 if(throwExceptionIfNoHandlerFound){
 5 throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request), (new ServletServerHttpRequest(request)).getHeaders());
 6 } else{
 7 //response.sendError(404);
 8 // return;
 9 // 修改为
10 // 定义一个请求路径为404的Controller方法
11 response.sendRedirect(request.getContextPath() + "/404"); 
12 }
13 }

项目中异常处理方案

 1@Component
 2public class GlobalExceptionResolver implements HandlerExceptionResolver {
 3
 4 public static final String AJAX="X-Requested-With";
 5
 6 public ModelAndView resolveException(HttpServletRequest request,
 7 HttpServletResponse response, Object handler, Exception ex) {
 8 // 区分是否是ajax请求 
 9 boolean isAjax = isAjax(request);
10 return handleException(request,response,handler,ex,isAjax);
11 }
12 /**
13 * 判断当前请求是否为异步请求.
14 * @param request
15 * @param response
16 * @return boolean
17 * @author zhaoxin
18 * @date 2018年9月18日 上午10:59:35
19 */
20 private boolean isAjax(HttpServletRequest request){
21 return StrUtil.isNotBlank(request.getHeader(AJAX));
22 }
23 /**
24 * 处理异常
25 * @return ModelAndView
26 * @author zhaoxin
27 * @date 2018年9月18日 上午11:52:40
28 */
29 private ModelAndView handleException(HttpServletRequest request,
30 HttpServletResponse response, Object handler,
31 Throwable ex, boolean isajax) {
32 //异常信息记录到日志文件中
33 LogUtil.logError("Capture global exceptions:"+ex.getMessage());
34 LogUtil.logException(ex);
35 //分普通请求和ajax请求分别处理
36 if(isajax){
37 return handleAjax(request,response,handler,ex);
38 }else{
39 return handlerNotAjax(request,response,handler,ex);
40 }
41 }
42
43 /**
44 * ajax异常处理并返回.
45 * @param request
46 * @param response
47 * @param handler
48 * @param initialEx
49 */
50 private ModelAndView handleAjax(HttpServletRequest request,
51 HttpServletResponse response, Object handler,Throwable initialEx){
52 response.setHeader("Cache-Control", "no-store");
53 // 返回JsonView
54 ModelAndView mav = new ModelAndView(new FastJsonJsonView());
55 mav.addObject("msg", "System exceptions please check logs");
56 mav.addObject("status", Const.RespondeCode.ERROR.getCode());
57 mav.addObject("success",false);
58 return mav;
59 }
60 /**
61 * 普通页面异常处理并返回.
62 * @param request
63 * @param response
64 * @param handler
65 * @param deepestException
66 * @return
67 */
68 private ModelAndView handlerNotAjax(HttpServletRequest request,HttpServletResponse response, Object handler, Throwable ex) {
69 Map<String, Object> model = new HashMap<>();
70 model.put("message", "System exceptions please check logs");
71 model.put("ex", ex);
72 return new ModelAndView("common/error500", model);
73 }
74
75
76}