目录
一、Controller类层级异常处理:@ExceptionHandler
前言
在Web项目中,未处理的异常返回到前端的错误信息是状态码500的内部服务器错误。如下请求映射方法中的处理就会给前端返回一个500的错误:
@RequestMapping("errorHandler")
@ResponseBody
public void errorTest() {
int i = 1 / 0;
}
以上的错误显示对于终端用户来说无法识别,很不友好。在一般的Web项目中的处理方式是通过try catch捕捉异常后转到自定义的错误页面。Spring MVC提供了更优雅的方式来处理异常,可以在Controller类层级进行处理,也可以在整个应用中进行全局的处理。
一、Controller类层级异常处理:@ExceptionHandler
@ExceptionHandler注解在某个Controller类的方法中使用时,用于处理该控制类中的处理器方法出现异常时的情况。使用起来也很简单,如下:
@ExceptionHandler
public ModelAndView exceptionHandler(Exception ex) {
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
@ExceptionHandler由ExceptionHandlerExceptionResolver处理器进行处理,这个处理是在DispatcherServlet中默认创建的。@ExceptionHandler默认会处理所有的异常,也可以指定处理指定类型的异常,例如:
@ExceptionHandler({ ArithmeticException.class })
//如果出现其他异常则不会走该注解的方法
@ExceptionHandler只能对当前Controller类的异常进行处理,如果整个应用的Controller异常需要统一处理。就需要在所有Controller中进行定义。虽然可以通过控制器基类的方式来解决这个问题,但这样Controller的定义会出现依赖,而且比较麻烦,此外还有可能存在有的Controller无法继承的状况。比较好的解决方式是使用全局的异常处理配置方式。
二、全局的异常处理
1.@ControllerAdvice
控制器切面注解(@ControllerAdvice)用在类中时,该注解可以作为全局的异常处理类,在类中用@ExceptionHandler注解的方法可以处理所有Controller发生的异常。示例如下:
@ControllerAdvice // 控制器切面注解
public class MyControllerAdvice {
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(Exception ex) { // 异常处理方法
ModelAndView mv = new ModelAndView();
mv.addObject("exception", ex);
return mv;
}
}
在实际开发中,@ControllerAdvice注解类中有多个@ExceptionHandler注解的方法,对应处理不同类型的异常处理。@ControllerAdvice是控制器的切面注解,内部使用的是AOP技术。
2.XML配置异常处理
除了使用注解之外,也可以在XML配置文件中配置SimpleMappingExceptionResolver类型的异常处理器Bean。如下:
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="defaultErrorView"> <!-- 异常处理视图名 -->
<value>error</value>
</property>
<property name="defaultStatusCode"> <!-- 异常状态码 -->
<value>500</value>
</property>
<property name="warnLogCategory"> <!-- 日志分类 -->
<value>org.springframework.web.servlet.handler.SimpleMappingExceptionResolver</value>
</property>
<!--异常处理页面用来获取异常信息的变量名,默认为exception-->
<property name="exceptionAttribute" value="ex"/>
</bean>
3.自定义全局异常处理器
实现Spring的异常处理接口HandlerExceptionResolver自定义全局异常处理器。
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
ModelAndView mv = new ModelAndView();
if (ex instanceof MyException) {
MyException myException = (MyException) ex;
// 处理特殊的异常……
} else {
// 其他异常均使用相同的处理方法
mv.addObject("exception", ex);
mv.setViewName("error");
}
return mv;
}
}
然后需要在配置文件中配置该Bean
<bean class="com.mec.springmvc.model.MyExceptionResolver"></bean>
三、Spring MVC的请求转发与重定向
请求和重定向都是Web中资源跳转的方式,只不过请求的转发是用户只发起了一次请求,它是服务器从一个资源跳转到另外一个资源,request对象还是原来的没有变,浏览器地址栏不发生变化。重定向是浏览器发起一次请求,服务器给浏览器回馈一个重定向的资源地址,浏览器对这个回馈的地址发起一个新的请求,故重定向相当于是浏览器发起了两个请求。在Spring MVC中有如下几种方式来实现请求的转发与重定向。
1.返回String方式
请求映射方法返回String类型,这种方式有个前提条件就是,当前的的Controller类不能被@RestController注解,还有请求映射方法不能被@ResponseBody注解,这都会被Spring MVC认为你想返回一个JSON字符串。
进行请求的转发与重定向时可以利用forward和redirect实现,如下:
@RequestMapping("/toforward01")
public String toForward01() {
return "forward:/toforward02";
}
@RequestMapping("/toforward02")
public String toForward02() {
return "forward:/WEB-INF/hello.jsp";
}
上述两个方法,当浏览器访问/toforward01,则请求先被转发到/toforward02处理,再由/toforward02转发到WEB-INF目录下的hello.jsp中。
但是由于我已经配置了视图解析器,视图解析器默认处理的就是请求的转发,配置如下:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 视图前缀 -->
<property name="prefix" value="/WEB-INF/"></property>
<!-- 视图后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
故在toForward02()方法中可以直接返回视图名称(hello),Spring MVC会自行加上配置的前缀和后缀,将请求转发至该视图,修改后的方法如下:
@RequestMapping("/toforward02")
public String toForward02() {
return "hello";
}
对于请求的重定向,可以使用redirect来实现,如下将请求重定向至page.html页面:
@RequestMapping("/toRedirect")
public String toRedirect() {
return "redirect:/html/page.html";
}
对于请求的重定向需要注意的是:
- 不能重定向至WEB-INF目录下的资源,因为重定向相当于浏览器发起了两次请求,第二次请求的就是重定向的location,浏览器是不能直接访问WEB-INF下的资源的,所以如果直接重定向至WEB-INF下的资源,相当于浏览器直接访问该资源,则会返回404错误。
- 即使配置了视图解析器,"redirect"也不能省略,这不是请求的转发,视图解析器默认是对请求转发的处理,这一点根据地址栏变化就可以看出来。
2.返回ModelAndView类型
该方式与返回String的方式差不多,这两个原理是一样的,返回String在框架内部也会解析为ModelAndView类型,将该String作为视图名。如下:
@RequestMapping("/toforward01")
public ModelAndView toForward01() {
ModelAndView mv = new ModelAndView();
mv.setViewName("forward:/toforward02");
return mv;
}
@RequestMapping("/toforward02")
public ModelAndView toForward02() {
ModelAndView mv = new ModelAndView();
mv.setViewName("hello");
return mv;
}
@RequestMapping("/toRedirect")
public ModelAndView toRedirect() {
ModelAndView mv = new ModelAndView();
mv.setViewName("redirect:/index.jsp");
return mv;
}
3.使用request和response对象
请求映射方法入参可以携带HttpServletRequest和HttpServletResponse对象,那么利用这两个对象使用原生方式也可以进行请求的转发与重定向,如下:
@RequestMapping("/toforward01")
public void toForward01(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.getRequestDispatcher("/toforward02").forward(request, response);
}
@RequestMapping("/toforward02")
public void toForward02(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.getRequestDispatcher("/WEB-INF/hello.jsp").forward(request, response);
}
@RequestMapping("/toRedirect")
public void toRedirect(HttpServletResponse response) throws IOException {
response.sendRedirect("html/page.html");
}