java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

十二、 拦截器

1、自定义拦截器概述

  1. Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口
    1. preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false
    2. postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。

afterCompletion():这个方法 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。

 

2、实验代码(单个拦截器)

  • 自定义拦截器类

package com.atguigu.springmvc.interceptors;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

 

public class FirstHandlerInterceptor implements HandlerInterceptor {

 

@Override

public void afterCompletion(HttpServletRequest arg0,

HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {

System.out.println(this.getClass().getName() + " - afterCompletion");

}

 

@Override

public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,

Object arg2, ModelAndView arg3) throws Exception {

System.out.println(this.getClass().getName() + " - postHandle");

}

 

@Override

public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,

Object arg2) throws Exception {

System.out.println(this.getClass().getName() + " - preHandle");

return true;

}

  • 配置拦截器

<mvc:interceptors>

<!-- 声明自定义拦截器 -->

<bean id="firstHandlerInterceptor"

      class="com.atguigu.springmvc.interceptors.FirstHandlerInterceptor"></bean>

</mvc:interceptors>

  • 断点调试拦截器执行流程

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  • 拦截器方法执行顺序(小总结)

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

 

3、实验代码(多个拦截器)

  • 自定义拦截器类(两个)

package com.atguigu.springmvc.interceptors;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

 

public class FirstHandlerInterceptor implements HandlerInterceptor {

 

@Override

public void afterCompletion(HttpServletRequest arg0,

HttpServletResponse arg1, Object arg2, Exception arg3)

throws Exception {

System.out.println(this.getClass().getName() + " - afterCompletion");

}

 

@Override

public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,

Object arg2, ModelAndView arg3) throws Exception {

System.out.println(this.getClass().getName() + " - postHandle");

}

 

@Override

public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,

Object arg2) throws Exception {

System.out.println(this.getClass().getName() + " - preHandle");

return true;

}

 

}

package com.atguigu.springmvc.interceptors;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

 

public class SecondHandlerInterceptor implements HandlerInterceptor {

 

@Override

public void afterCompletion(HttpServletRequest arg0,

HttpServletResponse arg1, Object arg2, Exception arg3)

throws Exception {

System.out.println(this.getClass().getName() + " - afterCompletion");

}

 

@Override

public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1,

Object arg2, ModelAndView arg3) throws Exception {

System.out.println(this.getClass().getName() + " - postHandle");

}

 

@Override

public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1,

Object arg2) throws Exception {

System.out.println(this.getClass().getName() + " - preHandle");

return true;

}

 

}

  • 配置自定义拦截器

<mvc:interceptors>

<!-- 声明自定义拦截器 -->

<bean id="firstHandlerInterceptor"

  class="com.atguigu.springmvc.interceptors.FirstHandlerInterceptor"></bean>

<!-- 配置拦截器引用 -->

<mvc:interceptor>                        

<mvc:mapping path="/empList"/>

<!-- <mvc:exclude-mapping path="/empList"/> -->

<bean id="secondHandlerInterceptor"

         class="com.atguigu.springmvc.interceptors.SecondHandlerInterceptor"></bean>

</mvc:interceptor>

</mvc:interceptors>

两个都是返回true :

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle

com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - preHandle

************************************biz method*******************************

com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - postHandle

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - postHandle

com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - afterCompletion

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion

两个都是返回false:

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle

true,false

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle

com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - preHandle

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion

false,true

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle

 

 

4、多个拦截方法的执行顺序

  • 关于执行顺序

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - preHandle

com.atguigu.springmvc.interceptors.SecondHandlerInterceptor – preHandle

************************************biz method*******************************

com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - postHandle

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - postHandle

com.atguigu.springmvc.interceptors.SecondHandlerInterceptor - afterCompletion

com.atguigu.springmvc.interceptors.FirstHandlerInterceptor - afterCompletion

  • 执行顺序图解

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

  • 从源代码的执行角度分析流程:

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {

if (getInterceptors() != null) {

for (int i = 0; i < getInterceptors().length; i++) {

HandlerInterceptor interceptor = getInterceptors()[i];

if (!interceptor.preHandle(request, response, this.handler)) {

triggerAfterCompletion(request, response, null);

return false;

}

this.interceptorIndex = i;

}

}

return true;

}

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {

if (getInterceptors() == null) {

return;

}

for (int i = getInterceptors().length - 1; i >= 0; i--) {

HandlerInterceptor interceptor = getInterceptors()[i];

interceptor.postHandle(request, response, this.handler, mv);

}

}

 

void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)

throws Exception {

 

if (getInterceptors() == null) {

return;

}

for (int i = this.interceptorIndex; i >= 0; i--) {

HandlerInterceptor interceptor = getInterceptors()[i];

try {

interceptor.afterCompletion(request, response, this.handler, ex);

}

catch (Throwable ex2) {

logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);

}

}

}

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  • 源码分析:分析interceptorIndex的值情况

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

 

 

十三、 异常处理 

1、异常处理概述

  1. Spring MVC 通过 HandlerExceptionResolver  处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
  2. SpringMVC 提供的 HandlerExceptionResolver 的实现类

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

2、HandlerExceptionResolver

DispatcherServlet  默认装配的 HandlerExceptionResolver

  • 没有使用 <mvc:annotation-driven/> 配置:

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  • 使用了 <mvc:annotation-driven/> 配置

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

ExceptionHandlerExceptionResolver是处理带有注释@ExceptionHandler(value={Exception.class})异常的

ResponseStatusExceptionResolver是处理带有注释@ResponseStatus(reason="runtionError", value=HttpStatus.NOT_FOUND)
class myRuntimeExceptionClass extends RuntimeException{ 的自定义异常类的

除去这两种以外的   都由默认的异常处理器DefaultHandlerExceptionResolver来处理

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

3、实验代码

  • 页面链接

<a href="testExceptionHandlerExceptionResolver?i=1">testExceptionHandlerExceptionResolver</a>

  • 控制器方法

package com.atguigu.springmvc.crud.handlers;

 

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;

 

@Controller

public class ExceptionHandler {

@RequestMapping("/testExceptionHandlerExceptionResolver")

public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){

System.out.println("10/"+i+"="+(10/i));

return "success";

}

}

  • 测试,出现异常情况

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

处理异常,跳转到error.jsp

  • 在控制器中增加处理异常的方法

@ExceptionHandler(value={java.lang.ArithmeticException.class})

public String handleException(Exception ex){

System.out.println("出现异常啦L"+ex);

return "error";

}

  • 增加error.jsp

<h3>Error Page</h3>

 

4、如何将异常对象从控制器携带给页面,做异常信息的获取

  • 异常对象不能通过Map集合方式传递给成功页面。(Map不可以

// @ExceptionHandler(value={java.lang.ArithmeticException.class})

public String handleException(Exception ex,Map<String,Object> map){

System.out.println("出现异常啦:"+ex);

map.put("exception",ex);

return "error";

}

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  • 可以通过ModelAndView将异常对象传递给成功页面上

@ExceptionHandler(value={java.lang.ArithmeticException.class})

public ModelAndView handleException(Exception ex){

System.out.println("出现异常啦:"+ex);

ModelAndView mv = new ModelAndView("error");

mv.addObject("exception", ex);

return mv;

}

 

5、匹配异常类型,执行顺序问题

@ExceptionHandler(value={java.lang.ArithmeticException.class})

public ModelAndView handleException(Exception ex){

System.out.println("出现异常啦:"+ex);

ModelAndView mv = new ModelAndView("error");

mv.addObject("exception", ex);

return mv;

}

 

@ExceptionHandler(value={java.lang.RuntimeException.class})

public ModelAndView handleException2(Exception ex){

System.out.println("RuntimeException-出现异常啦:"+ex);

ModelAndView mv = new ModelAndView("error");

mv.addObject("exception", ex);

return mv;

}

 

 6、公共的处理异常的类@ControllerAdvice

  1. 定义公共的处理异常的类

package com.atguigu.springmvc.exceptionAdvice;

 

import org.springframework.web.bind.annotation.ControllerAdvice;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.servlet.ModelAndView;

 

@ControllerAdvice

public class ExceptionAdviceHandler {

 

/*        

@ExceptionHandler(value={java.lang.ArithmeticException.class})

public ModelAndView handleException(Exception ex){

 

System.out.println("出现异常啦:"+ex);

ModelAndView mv = new ModelAndView("error");

mv.addObject("exception", ex);

 

return mv; 

}*/

 

@ExceptionHandler(value={java.lang.RuntimeException.class})

public ModelAndView handleException2(Exception ex){

 

System.out.println("RuntimeException-出现异常啦:"+ex);

ModelAndView mv = new ModelAndView("error");

mv.addObject("exception", ex);

 

return mv;

}

}

 

7、ExceptionHandlerExceptionResolver

  1. 主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。
  2. @ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,但是声明的异常有 RuntimeException 和 Exception,此候会根据异常的最近继承关系找到继承深度最浅的那个 @ExceptionHandler 注解方法,即标记了 RuntimeException 的方法
  3. ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找 @ControllerAdvice 中的@ExceptionHandler 注解方法

 

8、异常处理_ResponseStatusExceptionResolver

  1. 在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。
  2. 定义一个 @ResponseStatus 注解修饰的异常类
  3. 若在处理器方法中抛出了上述异常:若ExceptionHandlerExceptionResolver 不解析上述异常。由于触发的异常 UnauthorizedException 带有@ResponseStatus 注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus.UNAUTHORIZED 代码给客户端。HttpStatus.UNAUTHORIZED 代表响应码401,无权限。 关于其他的响应码请参考 HttpStatus 枚举类型源码。

 

org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver

Implementation of the HandlerExceptionResolver interface that uses the @ResponseStatus annotation to map exceptions to HTTP status codes.

This exception resolver is enabled by default in the

 org.springframework.web.servlet.DispatcherServlet.

 

 

9、实验代码

  • 页面链接

<a href="testResponseStatusExceptionResolver?i=10">testResponseStatusExceptionResolver</a>

  • 自定义异常类

package com.atguigu.springmvc.exception;

 

import org.springframework.http.HttpStatus;

import org.springframework.web.bind.annotation.ResponseStatus;

 

/**

 * 自定义异常类

HttpStatus.FORBIDDEN 不允许的,禁用的

 */

@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配")

public class UsernameNotMatchPasswordException extends RuntimeException{}

  • 控制器方法

@RequestMapping(value="/testResponseStatusExceptionResolver")

public String testResponseStatusExceptionResolver(@RequestParam("i") int i){

if(i==13){

throw new UsernameNotMatchPasswordException();

}

System.out.println("testResponseStatusExceptionResolver...");

return "success";

}

  • 出现的错误消息
  1. 没使用注解时:@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配")

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  1. 使用注解时:@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配")

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  1. 测试在方法上使用注解

@ResponseStatus(value=HttpStatus.NOT_FOUND,reason="测试方法上设置响应状态码")

@RequestMapping(value="/testResponseStatusExceptionResolver")

public String testResponseStatusExceptionResolver(@RequestParam("i") int i){

if(i==13){

throw new UsernameNotMatchPasswordException();

}

System.out.println("testResponseStatusExceptionResolver...");

return "success";

}

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  1. ResponseStatus

package org.springframework.web.bind.annotation;

 

import java.lang.annotation.Documented;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

 

import org.springframework.http.HttpStatus;

 

/**

 * Marks a method or exception class with the status code and reason that should be returned. The status code is applied

 * to the HTTP response when the handler method is invoked, or whenever said exception is thrown.

 *

 * @author Arjen Poutsma

 * @see org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver

 * @since 3.0

 */

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface ResponseStatus {

 

HttpStatus value();

 

String reason() default "";

 

}

  1. HttpStatus

 

 

10、异常处理_DefaultHandlerExceptionResolver

  1. 对一些特殊的异常进行处理,比如:
    1. NoSuchRequestHandlingMethodException、
    2. HttpRequestMethodNotSupportedException
    3. HttpMediaTypeNotSupportedException、
    4. HttpMediaTypeNotAcceptableException等。
  2. javadoc

org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

Default implementation of the HandlerExceptionResolver interface that resolves standard Spring exceptions and translates them to corresponding HTTP status codes.

This exception resolver is enabled by default in the org.springframework.web.servlet.DispatcherServlet.

 

 

11、实验代码

  • 增加页面链接:GET请求

<a href="testDefaultHandlerExceptionResolver">testDefaultHandlerExceptionResolver</a>

增加处理器方法

//@RequestMapping(value="/testDefaultHandlerExceptionResolver")

@RequestMapping(value="/testDefaultHandlerExceptionResolver",method=RequestMethod.POST)  //不支持GET请求

public String testDefaultHandlerExceptionResolver(){

System.out.println("testDefaultHandlerExceptionResolver...");

return "success";

}

  • 出现异常错误

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  •  出现异常交给DefaultHandlerExceptionResolver处理

@Override

protected ModelAndView doResolveException(

HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

 

try {

if (ex instanceof NoSuchRequestHandlingMethodException) {

return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,

handler);

}

else if (ex instanceof HttpRequestMethodNotSupportedException) {

return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,

response, handler);

}

else if (ex instanceof HttpMediaTypeNotSupportedException) {

return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,

handler);

}

else if (ex instanceof HttpMediaTypeNotAcceptableException) {

return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,

handler);

}

else if (ex instanceof MissingServletRequestParameterException) {

return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,

response, handler);

}

else if (ex instanceof ServletRequestBindingException) {

return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,

handler);

}

else if (ex instanceof ConversionNotSupportedException) {

return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);

}

else if (ex instanceof TypeMismatchException) {

return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);

}

else if (ex instanceof HttpMessageNotReadableException) {

return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);

}

else if (ex instanceof HttpMessageNotWritableException) {

return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);

}

else if (ex instanceof MethodArgumentNotValidException) {

return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler);

}

else if (ex instanceof MissingServletRequestPartException) {

return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler);

}

else if (ex instanceof BindException) {

return handleBindException((BindException) ex, request, response, handler);

}

else if (ex instanceof NoHandlerFoundException) {

return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);

}

}

catch (Exception handlerException) {

logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);

}

return null;

}

 

12、异常处理_SimpleMappingExceptionResolver

如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

实验代码

  • 增加页面链接

<a href="testSimpleMappingExceptionResolver?i=1">testSimpleMappingExceptionResolver</a>

  • 增加控制器方法

@RequestMapping("/testSimpleMappingExceptionResolver")

public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){

System.out.println("testSimpleMappingExceptionResolver..."); 

String[] s = new String[10]; 

System.out.println(s[i]); 

return "success";

}

  • 出现异常情况:参数i的值大于10

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

  • 配置异常解析器:自动将异常对象信息,存放到request范围内

<!-- 配置SimpleMappingExceptionResolver异常解析器 -->

<bean id="simpleMappingExceptionResolver"

 class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

<!-- exceptionAttribute默认值(通过ModelAndView传递给页面):

exception   ->  ${requestScope.exception}

public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";

-->

<property name="exceptionAttribute" value="exception"></property>

<property name="exceptionMappings">

<props>

<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>

</props>

</property>

</bean>

 

error.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"

    pageEncoding="UTF-8"%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

 "http://www.w3.org/TR/html4/loose.dtd">

<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<title>Insert title here</title>

</head>

<body> 

<h3>Error Page</h3> 

${exception }

${requestScope.exception } 

</body>

</html>

 

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

  • 源码分析

SimpleMappingExceptionResolver    L187 L339 

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

@Override

protected ModelAndView doResolveException(HttpServletRequest request,

 HttpServletResponse response,Object handler, Exception ex) {

 

// Expose ModelAndView for chosen error view.

String viewName = determineViewName(ex, request);

if (viewName != null) {

// Apply HTTP status code for error views, if specified.

// Only apply it if we're processing a top-level request.

Integer statusCode = determineStatusCode(request, viewName);

if (statusCode != null) {

applyStatusCodeIfPossible(request, response, statusCode);

}

return getModelAndView(viewName, ex, request);

}else {

return null;

}

}

 

/**

 * Return a ModelAndView for the given view name and exception.

 * <p>The default implementation adds the specified exception attribute.

 * Can be overridden in subclasses.

 * @param viewName the name of the error view

 * @param ex the exception that got thrown during handler execution

 * @return the ModelAndView instance

 * @see #setExceptionAttribute

 */

protected ModelAndView getModelAndView(String viewName, Exception ex) {

ModelAndView mv = new ModelAndView(viewName);

if (this.exceptionAttribute != null) {

if (logger.isDebugEnabled()) {

logger.debug("Exposing Exception as model attribute '" + this.exceptionAttribute + "'");

}

mv.addObject(this.exceptionAttribute, ex);

}

return mv;

}

 

 

 

十四、 运行流程图解 

1、流程图   

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

2、Spring工作流程描述

  1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获;
  2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI:

判断请求URI对应的映射

    • 不存在:
      1. 再判断是否配置了mvc:default-servlet-handler
      2. 如果没配置,则控制台报映射查找不到,客户端展示404错误
      3. 如果有配置,则执行目标资源(一般为静态资源,如:JSP,HTML
    • 存在:
      1. 执行下面流程
  1. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
  2. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter
  3. 如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法【正向】
  4. 提取Request中的模型数据,填充Handler入参,开始执行HandlerController)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
    • HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
    • 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
    • 数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
    • 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
  5. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
  6. 此时将开始执行拦截器的postHandle(...)方法【逆向】
  7. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet,根据ModelView,来渲染视图
  8. 在返回给客户端时需要执行拦截器的AfterCompletion方法【逆向】
  9. 将渲染结果返回给客户端

 

 

3、源码解析

1、搭建环境

 

  • 拷贝jar包

spring-aop-4.0.0.RELEASE.jar

spring-beans-4.0.0.RELEASE.jar

spring-context-4.0.0.RELEASE.jar

spring-core-4.0.0.RELEASE.jar

spring-expression-4.0.0.RELEASE.jar

commons-logging-1.1.3.jar

spring-web-4.0.0.RELEASE.jar

spring-webmvc-4.0.0.RELEASE.jar

  • 配置文件web.xml

<servlet>

<servlet-name>springDispatcherServlet</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:springmvc.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>springDispatcherServlet</servlet-name>

<url-pattern>/</url-pattern>

</servlet-mapping>

  • 配置文件springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:mvc="http://www.springframework.org/schema/mvc"

xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

 

<!-- 设置扫描组件的包 -->

<context:component-scan base-package="com.atguigu.springmvc"/>

 

<!-- 配置视图解析器 -->

<bean id="internalResourceViewResolver"

    class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="prefix" value="/WEB-INF/views/"/>

<property name="suffix" value=".jsp"/>

</bean>

 

</beans>

2、完成HelloWorld

  • 页面链接

<a href="helloworld">Hello World</a>

  • 控制器方法

package com.atguigu.springmvc.handler;

 

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

 

@Controller

public class HelloWorldHandler { 

@RequestMapping("/helloworld")

public String testHello(){ 

System.out.println("Hello,SpringMVC..."); 

return "success";

}

  • 成功页面:/views/success.jsp

<h3>Success Page</h3>

 

3、Debug实验

  • 正常流程,运行出结果
  • 没有配置<mvc:default-servlet-handler/>,测试,直接报404
  1. http://localhost:8080/SpringMVC_09_WorkFlow/helloworld2

四月 20, 2016 11:53:19 上午 org.springframework.web.servlet.PageNotFound noHandlerFound

警告: No mapping found for HTTP request with URI [/SpringMVC_09_WorkFlow/helloworld2] in DispatcherServlet with name 'springDispatcherServlet'

  1. http://localhost:8080/SpringMVC_09_WorkFlow/test.html

四月 20, 2016 11:54:16 上午 org.springframework.web.servlet.PageNotFound noHandlerFound

警告: No mapping found for HTTP request with URI [/SpringMVC_09_WorkFlow/test.html] in DispatcherServlet with name 'springDispatcherServlet'

  • 配置<mvc:default-servlet-handler/>,测试,会去查找目标资源
  • 测试,依然发生错误,这时,需要配置:<mvc:annotation-driven/>,否则,映射解析不好使。

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

 

4、Debug流程分析

  • HandlerExecutionChain mappedHandler;包含了拦截器和处理器方法;

DispatcherServlet L902 916

org.springframework.web.servlet.HandlerExecutionChain

Handler execution chain, consisting of handler object and any handler interceptors. Returned by

 HandlerMapping's HandlerMapping.getHandler method.

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

  • HandlerMapping

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结 org.springframework.web.servlet.HandlerMapping

Interface to be implemented by objects that define a mapping between requests and handler objects.

This class can be implemented by application developers, although this is not necessary, as org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping and org.springframework.web.servlet.handler.SimpleUrlHandlerMapping are included in the framework. The former is the default if no HandlerMapping bean is registered in the application context.

HandlerMapping implementations can support mapped interceptors but do not have to. A handler will always be wrapped in a HandlerExecutionChain instance, optionally accompanied by some HandlerInterceptor instances. The DispatcherServlet will first call each HandlerInterceptor's preHandle method in the given order, finally invoking the handler itself if all preHandle methods have returned true.

The ability to parameterize this mapping is a powerful and unusual capability of this MVC framework. For example, it is possible to write a custom mapping based on session state, cookie state or many other variables. No other MVC framework seems to be equally flexible.

Note: Implementations can implement the org.springframework.core.Ordered interface to be able to specify a sorting order and thus a priority for getting applied by DispatcherServlet. Non-Ordered instances get treated as lowest priority.

 

  • 没有配置<mvc:default-servlet-handler/>,<mvc:annotation-driven/>,发送一个不存在资源的请求路径,mappedHandler为null
  1. http://localhost:8080/SpringMVC_09_WorkFlow/helloworld2

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

  • 配置<mvc:default-servlet-handler/>,<mvc:annotation-driven/>,发送一个不存在资源的请求路径
  1. http://localhost:8080/SpringMVC_09_WorkFlow/helloworld2
  2. mappedHandler不为null,原因是当循环simpleUrlHandlerMapping时,当做静态资源处理

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结

 

5、断点

java学习笔记——springmvc 之 拦截器 与 异常处理 和 SpringMVC的运行流程总结