Spring3.0 对异常的处理方式总共有两种:
第一种是使用 HandlerExceptionResolver 接口,并且 Spring 已经提供默认的实现类 SimpleMappingExceptionResolver。
第二种方法是在 Controller 内部实现,灵活性更高。
这两种方式不能共存。我们一般在项目中使用第一种方法。
下面分别描述一下这两种使用方式:
1.1 基于 HandlerExceptionResolver 接口的方式
使用这种方式只需要实现 resolveException 方法,该方法返回一个 ModelAndView 对象,在方法内部对异常的类型进行判断,然后返回合适的 ModelAndView 对象,如果该方法返回了 null,则 Spring 会继续寻找其他的实现了 HandlerExceptionResolver 接口的 Bean。换句话说,Spring 会搜索所有注册在其环境中的实现了 HandlerExceptionResolver 接口的 Bean,逐个执行,直到返回了一个 ModelAndView 对象。
@Component
public class CustomExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
// 记录异常日志
Logger logger = LogManager.getLogger();
logger.error(e.getMessage(), e);
if(e instanceof IOException){//IO错误
return new ModelAndView("error/ioException","ex",e);
}else if(e instanceof SQLException){//SQL错误
return new ModelAndView("error/sqlException","ex",e);
}
return null;
}
}
这个类必须声明到 SpringMVC配置文件中,或者在类上使用 @Component 标签,让 Spring 管理它。
error/ioException.jsp:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
error/sqlException.jsp:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
1.2 直接使用Spring提供的默认实现类 SimpleMappingExceptionResolver
只需要使用注入到 Spring 配置文件进行声明即可。
<!-- 默认的实现类注入 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!--
定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
将不同的异常映射到不同的页面上。
-->
<property name="exceptionMappings">
<props>
<prop key="IOException">error/ioException</prop>
<prop key="java.sql.SQLException">error/sqlException</prop>
</props>
</property>
<!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
<property name="defaultErrorView" value="error/error"></property>
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
<property name="exceptionAttribute" value="ex"></property>
<!-- 定义默认返回状态码为500 -->
<property name="defaultStatusCode" value="500"></property>
</bean>
error/ioException.jsp:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
error/sqlException.jsp:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
error/error.jsp:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
1.3 覆盖默认实现类SimpleMappingExceptionResolver中的doResolveException方法
<!-- 异常捕获 -->
<bean id="exceptionResolver" class="com.westar.core.web.WebHandlerExceptionResolver">
<!--
定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
将不同的异常映射到不同的页面上。
-->
<property name="exceptionMappings">
<props>
<prop key="IOException">error/ioException</prop>
</props>
</property>
<!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
<property name="defaultErrorView" value="error/error"></property>
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
<property name="exceptionAttribute" value="ex"></property>
<!-- 定义默认返回状态码为500 -->
<property name="defaultStatusCode" value="500"></property>
</bean>
@Component
public class WebHandlerExceptionResolver extends SimpleMappingExceptionResolver {
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object object, Exception ex) {
// 记录异常日志
Logger logger = LogManager.getLogger();
logger.error(ex.getMessage(), ex);
if(ex instanceof SQLException){//SQL错误
return new ModelAndView("error/sqlException","ex",ex);
}else {//跳转到默认异常处理页面,异常信息变量为默认值
return super.doResolveException(request, response, handler, ex);
}
}
}
error/ioException.jsp:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
error/sqlException.jsp:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
error/error.jsp:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
1.4自定义实现类与默认的实现类同时使用。
XML配置:
<bean class="com.core.service.WebExceptionHandler">
<!--
定义此处无须处理的异常集合,直接返回null,交由其他的异常处理类处理
-->
<property name="excludedExceptions">
<list>
<value>java.io.IOException</value>
<value>java.sql.SQLException</value>
</list>
</property>
<!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
<property name="defaultErrorView" value="error/error"></property>
<!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
<property name="exceptionAttribute" value="ex"></property>
<!-- 定义默认返回状态码为500 -->
<property name="defaultStatusCode" value="500"></property>
</bean>
自定义异常处理:
@Component
public class CustomExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
// 判断是否已进行了异常日志的记录
String logging=(String)httpServletRequest.getAttribute("logging");
if(logging==null||logging==""){
Logger logger = LogManager.getLogger();
logger.error(e.getMessage(), e);
httpServletRequest.setAttribute("logging","true");
}
if(e instanceof IOException){//IO错误
return new ModelAndView("error/ioException","ex",e);
}else if(e instanceof SQLException){//SQL错误
return new ModelAndView("error/sqlException","ex",e);
}
return null;
}
}
重写默认异常处理:
public class WebExceptionHandler extends SimpleMappingExceptionResolver {
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 判断是否已进行了异常日志的记录
String logging=(String)request.getAttribute("logging");
if(logging==null||logging==""){
Logger logger = LogManager.getLogger();
logger.error(ex.getMessage(), ex);
request.setAttribute("logging","true");
}
return super.doResolveException(request, response, handler, ex);
}
}
若先执行自定义异常处理类,先判断是否已记录异常日志,当遇到IO、SQL错误返回到对应的错误页面;其他错误将交由默认异常处理类处理,异常处理类将不再进行日志记录并返回到通用的错误页面;
若先执行默认异常处理类,先判断是否已记录异常日志,当遇到IO、SQL错误返回null并交由自定义异常处理类,自定义异常处理类将不再进行日志记录并返回对应的错误页面;其他错误由默认异常处理类处理并返回到通用的错误页面;
异常显示界面:
<html>
<head><title>Exception!</title></head>
<body>
<% Exception ex = (Exception) request.getAttribute("ex"); %>
<H2>Exception: <%=ex.getMessage()%></H2>
<P><% ex.printStackTrace(new java.io.PrintWriter(out)); %></P>
</body>
</html>
2 Controller 内部单独实现
该方法需要定义在 Controller 内部,然后创建一个方法并用 @ExceptionHandler 注解来修饰用来处理异常,这个方法基本和 @RequestMapping 修饰的方法差不多,只是可以多一个类型为 Exception 的参数,@ExceptionHandler 中可以添加一个或多个异常的类型,如果为空的话则认为可以触发所有的异常类型错误。
@Controller
public class ExceptionHandlerController {
@RequestMapping("test")
public void Test(){
System.out.println("aaaaa");
}
//如果该类的某一个方法抛出了IOException或IOException,则由该方法处理
@ExceptionHandler(value={IOException.class,SQLException.class})
public String exp(Exception ex,HttpServletRequest request) {
request.setAttribute("ex", ex);
return "/error.jsp";
}
}
三、相关问题
HandlerExceptionResolver 和 web.xml 中配置的 error-page 会有冲突吗?
web.xml 中配置 error-page 同样是配置出现错误时显示的页面:
<error-page>
<error-code>500</error-code>
<location>/500.jsp</location>
</error-page>
如果 resolveException 返回了 ModelAndView,会优先根据返回值中的页面来显示。不过,resolveException 可以返回 null,此时则展示 web.xml 中的 error-page 的500状态码配置的页面。