springmvc错误处理
-
使用 @ ExceptionHandler 注解
-
实现 HandlerExceptionResolver 接口, 并加入spring容器
-
使用 @controlleradvice 注解
就来看看这几种方式
1. 使用 @ ExceptionHandler 注解
HiController:
@Controller public class HiController { @RequestMapping("/hi") public ModelAndView getHi() { ModelAndView mav = new ModelAndView("me"); // String hi = "hi".substring(1,5); int a = 1/0; return mav; } @ExceptionHandler(value=StringIndexOutOfBoundsException.class) @ResponseBody public String handleStringException() { return "string out of bounds"; } @ExceptionHandler(value=ArithmeticException.class) @ResponseBody public String handleException() { return "number illegal"; } }
再写一个Controller:
@Controller public class MyController { @RequestMapping("/hello") public String getHello() { int a = 1/0; return "say Hello"; } }
结果:
这种只能处理@ExceptionHandler所在Handler的异常。别的Handler的异常不能处理。 想定义全局的,这种每个Controller都要写一遍,所以并不好用
看了一下源码,DispatcherServlet的catch里做了一些异常处理,直到这里:
拿到的是这个Handler(HiController)的ExceptionHandlerMethodResolver, 如果进的是/mvc/hello (另一个Controller), resolver就是空的。 所以它只能执行对应的Controller
后面做的事情,大概就是找到match的Exception,然后通过反射调用这个handleStringException,再处理对应的ModelAndView:
具体debug看一下:
通过抛出的异常,找到对应的方法:返回的 method 是 public java.lang.String com.springmvc.HiController.handleException(),
再通过反射调用HiController.handleException()方法,
2. 实现 HandlerExceptionResolver 接口
代码:
1.还是用上面两个Controller
2. MyHandlerExceptionResolver :
//@Component public class MyHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mav = new ModelAndView("exception"); return mav; } }
3. springMVC.xml:
<bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"></property>
<property name="suffix" value=".jsp"></property>
<property name="cache" value="false"></property>
</bean>
<bean name="handlerExceptionResolver" class="exception.MyHandlerExceptionResolver"/>
jsp:
访问:
这里使用@Component没有效果,可以看到springMVC容器中根本没有自定义的ExceptionResolver,很奇怪使用@Component为什么没有加入到容器中? 但是使用xml能加入到容器,所以xml的没问题
先看看上面用到的Exception的继承关系
HandlerExceptionResolver接口就一个方法:
public interface HandlerExceptionResolver { @Nullable ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex); }
Q1:自定义的handlerExceptionResolver是怎么注册的?
启动spring的时候注册的, 调用到Listener
从spring容器中获取type为org.springframework.web.servlet.HandlerExceptionResolver的bean:
Q2: 为什么使用Component 自定义的ExceptionResolver没有注册到容器中?
今天再看, 用另一个Controller看看容器中到底有没有这个bean, 结果是false, 一开始以为是bean的实例化顺序发现想法不对。
@RestController public class MyController { @Autowired ApplicationContext ac; @RequestMapping("/hello") public String getHello() { boolean b = ac.containsBean("myHandlerExceptionResolver"); System.out.println("contains: " + b); return "say Hello"; } }
再看原来是扫描包的问题:
<!--配置包扫描--> <context:component-scan base-package="com.springmvc"></context:component-scan> <context:component-scan base-package="inteceptor"></context:component-scan> <context:component-scan base-package="exception"></context:component-scan>
所以那天我自己实现interceptor接口也没成功。下一篇试试。
Q3: 这种怎么对不同的exception进入不同的处理页面?
3. 使用 @controlleradvice 注解
controlleradvice看完aop再看是为什么?
先实验一下就完
代码:
ExceptionController:
@ControllerAdvice public class ExceptionController { @ExceptionHandler(value=ArithmeticException.class) @ResponseBody public String handleArithmeticException() { return "math illegal"; } @ExceptionHandler(value=StringIndexOutOfBoundsException.class) @ResponseBody public String handleStringException() { return "string out of bounds"; } }
测试用HiController:
@Controller public class HiController { @RequestMapping("/hi") public ModelAndView getHi() { ModelAndView mav = new ModelAndView("me"); int a = 1/0; return mav; } @RequestMapping("/hii") public ModelAndView getHii() { ModelAndView mav = new ModelAndView("me"); String hi = "hi".substring(1, 5); return mav; } }
结果, 不在同一个controller中能处理不同的exception: