目录
4、DefaultHandlerExceptionResolver
一、自定义拦截器
1、HandlerInterceptor 接口
HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。
preHandle(): 这个方法在业务处理器处理请求之前被调用,SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
postHandle():这个方法在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。
afterCompletion():该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
2、后台:
public class FirstInterceptor implements HandlerInterceptor {
/**
* 该方法在目标方法之前被调用.<br>
* 若返回值为 true, 则继续调用后续的拦截器和目标方法. <br>
* 若返回值为 false, 则不会再调用后续的拦截器和目标方法.
*
* 可以考虑做权限. 日志, 事务等.
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("[FirstInterceptor] preHandle");
return true;
}
/**
* 调用目标方法之后, 但渲染视图之前. <br>
* 可以对请求域中的属性或视图做出修改.
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[FirstInterceptor] postHandle");
}
/**
* 渲染视图之后被调用. 释放资源
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("[FirstInterceptor] afterCompletion");
}
}
3、配置拦截器
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!--配置作用的url
/**的意思是所有文件夹及里面的子文件夹
/*是所有文件夹,不含子文件夹
-->
<mvc:mapping path="/**"/>
<!-- 配置不作用的URL -->
<mvc:exclude-mapping path="/user"/>
<bean class="com.starfall.springmvc.interceptors.FirstInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
二、拦截器执行顺序
1、单个拦截器执行顺序
源码解析:
1、找到DispatcherServlet的doDispatch方法。
在第938行执行ha.handle之前,第932行会先执行applyPreHandle方法,看下applyPreHandle方法的详细信息:
可以看出,applyPreHandle方法是进行拦截器的遍历,依次执行所有的拦截器的interceptor.preHandle方法,如果preHandle返回false则中断遍历,并且返回false,并且不执行ha.handle的方法,请求被终止。
2、执行完preHandle方法后
回到DispatcherServlet的doDispatch方法,发现第947行,在执行完ha.handle的方法即请求方法后,会执行mappedHandler.applyPostHandle方法。查询该方法详细:
可以看出applyPostHandle方法是在遍历拦截器,执行interceptor.postHandle方法。并且是倒叙执行。
3、执行完postHandle方法后
继续回到DispatcherServlet的doDispatch方法,请求执行完毕进入processDispatchResult方法。
发现该方法在执行完视图的解析后,会执行triggerAfterCompletion方法
进入triggerAfterCompletion方法,发现同样是倒叙遍历拦截器,执行interceptor.afterCompletion方法。
2、多个拦截器
从图可以看出,当有多个拦截器同时工作时,它们的preHandle()方法会按照配置文件中拦截器的配置顺序执行,而它们的postHandle()方法和afterCompletion()方法则会按照配置顺序的反序执行。
测试:
当SecondInterceptor的preHandle返回false的时候:[FirstInterceptor] preHandle和[FirstInterceptor] afterCompletion会执行。
三、异常处理
Spring MVC 通过HandlerExceptionResolver处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。
SpringMVC 提供的HandlerExceptionResolver的实现类:
当<mvc:annotation-driven/> 没有配置的时候,DispatcherServlet 默认装配的 HandlerExceptionResolver是:
AnnotationMethodHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver
而使用了<mvc:annotation-driven/>注解后,装配的 HandlerExceptionResolve则变成了:
ExceptionHandlerExceptionResolver、ResponseStatusExceptionResolver、DefaultHandlerExceptionResolver。
对于这几种异常在下方的处理异常方式中会介绍。
1、基于控制器
此种类型的控制异常,需要在每个控制器下编写出异常的处理方法。
1、使用@ExceptionHandler注解,ExceptionHandlerExceptionResolver主要就是处理Handler中用@ExceptionHandler注解定义的方法。
2、使用@ResponseStatus注解,ResponseStatusExceptionResolver用来支持@ResponseStatus的使用,处理使用了@ResponseStatus注解的异常,根据注解的内容,返回相应的HTTP Status Code和内容给客户端。下面例子返回的页面就是如下情形:
3、@ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,但是声明的异常有RuntimeException和 Exception,此候会根据异常的最近继承关系找到继承深度最浅的那个@ExceptionHandler注解方法,即找到RuntimeException异常的方法。
/**
* 处理异常 使用@ExceptionHandler注解
*/
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "找不到页面")
@ExceptionHandler({ Exception.class })
public ModelAndView handleArithmeticException(Exception ex) {
ex.printStackTrace();
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
@RequestMapping("/testExceptionHandlerExceptionResolver")
public String testExceptionHandlerExceptionResolver() {
double a = 10 / 0;
return SUCCESS;
}
2、基于自定义的异常类
此种方法可以实现全局控制,但是需要编写多个自定义的异常类。并且需要在程序中主动捕捉、抛出异常。
主要使用@ResponseStatus注解,注解在自定义的异常类上。若ExceptionHandlerExceptionResolver 不解析异常。由于触发的自定义异常 带有@ResponseStatus注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus 的code和reason 代码给客户端。
@ResponseStatus(value=HttpStatus.FORBIDDEN, reason="用户名和密码不匹配!")
public class UserNameNotMatchPasswordException extends RuntimeException{
/**
*
*/
private static final long serialVersionUID = 1L;
}
@ResponseStatus(reason="测试",value=HttpStatus.NOT_FOUND)
@RequestMapping("/testResponseStatusExceptionResolver")
public String testResponseStatusExceptionResolver(@RequestParam("i") int i){
if(i == 13){
throw new UserNameNotMatchPasswordException();
}
System.out.println("testResponseStatusExceptionResolver...");
return "success";
}
3、统一全局处理异常
1、使用@ControllerAdvice+@ExceptionHandler+@ResponseStatus
自定义一个处理异常的类,注意不是异常类,使用@ControllerAdvice注解该类,并且在类中编写处理异常的方法,使用@ExceptionHandler,@ResponseStatus注解该方法。
@ControllerAdvice
public class GlobalExceptionResolver {
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "找不到页面")
@ExceptionHandler({ Exception.class })
public ModelAndView handleArithmeticException(Exception ex) {
ex.printStackTrace();
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
}
2、使用SimpleMappingExceptionResolver
它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
其提供的能力有:
- 根据异常的类型,将异常映射到视图;
- 可以为不符合处理条件没有被处理的异常,指定一个默认的错误返回;
- 处理异常时,记录log信息;
- 指定需要添加到Modle中的Exception属性,从而在视图中展示该属性。
配置文件形式:在SpringMVC配置文件中
<!-- 配置使用 SimpleMappingExceptionResolver 来映射异常 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--配置异常的属性值为exp,那么在错误页面中可以通过 ${exp} 来获取异常的信息 如果不配置这个属性,它的默认值为exception -->
<property name="exceptionAttribute" value="exp"></property>
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
继承SimpleMappingExceptionResolver形式
public class TestSimpleMappingExceptionResolver extends SimpleMappingExceptionResolver {
/**
* 重写Spring统一异常处理方法
*/
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
logger.info("-------------------------->系统异常", ex);
// ex.printStackTrace();
logger.info("<--------------------------");
StringBuffer requestUrl = request.getRequestURL();
String contextPath = request.getContextPath();
String path = requestUrl.substring(requestUrl.indexOf(contextPath) + contextPath.length() + 1);
String viewName = "error_all";
if (path.startsWith("mobile") || path.startsWith("m")) {
viewName = "error_all";
}
// 返回消息
ModelAndView mv = new ModelAndView();
mv.setViewName(viewName);
StringWriter sw = new StringWriter();
ex.printStackTrace(new PrintWriter(sw, true));
mv.addObject("exp", sw.toString());
// 判断是否异常处理
if (!(request.getHeader("accept").indexOf("application/json") > -1
|| (request.getHeader("X-Requested-With") != null
&& request.getHeader("X-Requested-With").indexOf("XMLHttpRequest") > -1))) {
request.setAttribute("info", "操作失败,系统发生异常。");
Integer statusCode = determineStatusCode(request, viewName);
if (statusCode != null) {
applyStatusCodeIfPossible(request, response, statusCode);
}
return mv;
} else {// JSON格式返回
// 否异步加载页面
if (request.getParameter("loadPage") != null) {
return mv;
}
// 非异步加载页面,返回信息
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", "01");
map.put("message", "异常");
return new ModelAndView(new MappingJacksonJsonView(), map);
}
}
}
3、实现 HandlerExceptionResolver 接口
@Component
public class ExceptionTest implements HandlerExceptionResolver{
/**
* TODO 简单描述该方法的实现功能(可选).
* @see org.springframework.web.servlet.HandlerExceptionResolver#resolveException(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.lang.Object, java.lang.Exception)
*/
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
System.out.println("This is exception handler method!");
return null;
}
}
4、DefaultHandlerExceptionResolver
DefaultHandlerExceptionResolver是HandlerExceptionResolver接口的默认实现,基本上是Spring MVC内部使用,用来处理Spring定义的各种标准异常,将其转化为相对应的HTTP Status Code。其处理的异常类型有:
NoSuchRequestHandlingMethodException
HttpRequestMethodNotSupportedException
HttpMediaTypeNotSupportedException
HttpMediaTypeNotAcceptableException
MissingServletRequestParameterException
ServletRequestBindingException
ConversionNotSupportedException
TypeMismatchException
HttpMessageNotReadableException
HttpMessageNotWritableException
MethodArgumentNotValidException
MissingServletRequestPartException
BindException
NoHandlerFoundException