异常处理器和拦截器
-
异常处理的思路:
- 系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生
- 系统的 dao、service、controller 出现都通过 throws Exception 向上抛出,最后由 springmvc 前端
控制器交由异常处理器进行异常处理
-
异常处理器的编写:
- 当程序发生错误时(异常会层层外抛),错误最终会传递给DispatcherServlet,由DispatcherServlet进行异常处理
- SpringMVC的异常处理机制处理异常的步骤:
- 创建自定义异常类
public class SysException extends Exception { // 存储提示信息的 private String message; // 构造方法 public SysException(String message) {this.message = message; } // get,set方法 public String getMessage() {return message; } public void setMessage(String message) {this.message = message; } }
- 创建异常处理器,异常处理器必须实现
HandlerExceptionResolver
接口,其resolveException()
方法执行异常处理// 自定义异常处理器 public class MyExceptionResolver implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { SysException e = null; if (ex instanceof SysException) { e = (SysException) ex; } else { e = new SysException("其他错误"); } ModelAndView mv = new ModelAndView(); // 封装错误信息 mv.addObject("errorMsg", e.getMessage()); // 跳转页面 mv.setViewName("error"); return mv; } }
- Spring容器中注入异常处理器
<!--配置异常处理器--> <bean id="myExceptionResolver" class="cn.maoritian.exception.MyExceptionResolver"/>
- 创建自定义异常类
-
SpringMVC中处理异常的三种方式:
-
第一种方式:使用
@ ExceptionHandler
注解 使用该注解有一个不好的地方就是:进行异常处理的方法必须与出错的方法在同一个Controller里面不能全局的控制异常@ExceptionHandler({MyException.class}) public String exception(MyException e) { System.out.println(e.getMessage()); e.printStackTrace(); return "exception"; }
-
第二种方式:使用
@ControllerAdvice 注解+@ExceptionHandler
@ ExceptionHandler
需要进行异常处理的方法必须与出错的方法在同一个Controller里面。那么当代码加入了@ControllerAdvice
,则不需要必须在同一个 controller 中了。这也是 Spring 3.2 带来的新特性上可以看出大体意思是控制器增强 可以实现全局的异常捕捉:@ExceptionHandler(value={java.lang.ArithmeticException.class}) public ModelAndView arithmeticExceptionHandler(Exception e){ ModelAndView mv = new ModelAndView(); mv.addObject("error", e.toString()+" -- advice"); mv.setViewName("error1"); return mv; }
通过@ControllerAdvice注解可以将对于控制器的全局配置放在同一个位置:注解了@ControllerAdvice的类的方法可以使用@ExceptionHandler、@InitBinder、@ModelAttribute注解到方法上:
- @ExceptionHandler:用于全局处理控制器里的异常。
- @InitBinder:用来设置WebDataBinder,用于自动绑定前台请求参数到Model中。
- @ModelAttribute:本来作用是绑定键值对到Model中,此处让全局的@RequestMapping都能获得在此处设置的键值对
-
如果 @ExceptionHandler 注解中未声明要处理的异常类型,则默认为参数列表中的异常类型。所以还可以写成这样:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler() @ResponseBody String handleException(Exception e){ return "Exception Deal! " + e.getMessage(); } }
-
第三种方式:实现
HandlerExceptionResolver
接口 项目中的异常需要统一处理,正常情况下,需要提前准备好一个错误页面,当项目出错了,将该页面展示给用户这个类必须声明到 Spring 配置文件中,或者使用 @Component 标签 或者是SimpleMappingExceptionResolver
这是HandlerExceptionResolver
接口实现类 使用时只需要使用注入到 Spring 配置文件进行声明即可// 自定义异常处理器 public class MyExceptionResolver implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { SysException e = null; if (ex instanceof SysException) { e = (SysException) ex; } else { e = new SysException("其他错误"); } ModelAndView mv = new ModelAndView(); // 封装错误信息 mv.addObject("errorMsg", e.getMessage()); // 跳转页面 mv.setViewName("error"); return mv; } }
SimpleMappingExceptionResolver
实现类使用方式:但是一般的都是使用第一种方式<!-- 自定义的实现类 --> <bean id="exceptionHandler" class="com.edu.CustomExceptionHandler"/> <!-- 默认的实现类注入 --> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 --> <property name="defaultErrorView" value="error"></property> <!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception --> <property name="exceptionAttribute" value="ex"></property> <!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值, 将不同的异常映射到不同的页面上。 --> <property name="exceptionMappings"> <props> <prop key="IOException">error/ioexp</prop> <prop key="java.sql.SQLException">error/sqlexp</prop> </props> </property> </bean>
-
第四种方式:配置errorpage
<!--通过状态码进行设置--> <error-page> <error-code>404</error-code> <location>/error/404.html</location> </error-page> <!--通过异常的类型进行配置--> <error-page> <exception-type>java.lang.Exception</exception-type> <location>/error/500.html</location> </error-page>
-
-
拦截器:
- 拦截器的配置:
- 自定义拦截器需要继承
HandlerInterceptor
接口,该接口中定义了三个方法,都已有其默认实现:preHandle(...)
: 该方法在处理器方法实际执行之前执行preHandle(..)
方法返回一个boolean值可以通过这个方法来决定是否继续执行处理链中的部件。当方法返回 true时,处理器链会继续执行;若方法返回 false, DispatcherServlet即认为拦截器自身已经完成了对请求的处理(比如说,已经渲染了一个合适的视图),那么其余的拦截器以及执行链中的其他处理器就不会再被执行了
postHandle(...)
: 该方法在处理器方法实际执行完毕以后执行afterCompletion(...)
: 该方法在整个请求处理完成后执行
- 自定义拦截器需要继承
- 具体的步骤:
- 拦截器的编写:
public class TimeBasedAccessInterceptor extends HandlerInterceptor { private int openingTime; private int closingTime; public void setOpeningTime(int openingTime) {this.openingTime = openingTime; } public void setClosingTime(int closingTime) {this.closingTime = closingTime; } public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Calendar cal = Calendar.getInstance(); int hour = cal.get(HOUR_OF_DAY); if (openingTime <= hour && hour < closingTime) { return true; } response.sendRedirect("http://host.com/outsideOfficeHours.html"); return false; } }
- Spring容器中注入拦截器 使用的标签是
<mvc:interceptors>
<mvc:interceptors> <mvc:interceptor> <!-- 拦截的方法 --> <mvc:mapping path="/**" /> <!-- 具体的拦截器 --> <bean id="officeHoursInterceptor" class="com.itheiam.interceptor.TimeBasedAccessInterceptor"> <property name="openingTime" value="9"/> <property name="closingTime" value="18"/> </bean> </mvc:interceptor> </mvc:interceptors>
- 拦截器的编写:
- 多个拦截器的执行顺序
- 多个拦截器的执行顺序是由外向内分层执行(在XML文件中先进行配置的先执行)
- 拦截器的配置:
-
自定义类型转换器:(将字符串转成)
** * 将字符串转换成日期类型 在springmvc配置文件中 进行配置自定义类型转换器 自定义的类型转换器同样的是一个对象 使用的是bean标签 */ public class StringToDateConverter implements Converter<String, Date> { /** * source 就是传入的值就是传入的值 * @param source * @return */ @Override public Date convert(String source) { if (source == null){ throw new RuntimeException("没有传值!"); }else{ // 使用DateFormat 接口来进行类型的转换 DateFormat df = new SimpleDateFormat("yyyy-MM-dd"); Date parse = null; try { parse = df.parse(source); } catch (Exception e) { throw new RuntimeException("数据类型转换出现错误!"); } return parse; } } }
进行自定义类型转换器的配置:
<!--配置自定义类型转换器 使用的标签是bean标签 属性是id 和 class属性--> <bean id="dateConverter" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <set> <!--将自定义的类型转换器注册到set中 第三步将转换器生效--> <bean class="com.itheima.utils.StringToDateConverter"></bean> </set> </property> </bean>