在SpringMVC中,提供了一个全局异常处理器,用于对系统中出现的异常进行统一处理。在一般的系统中,DAO层、Service层及Controller层出现异常都以“throws Exception”的形式向上层抛出,最后都会有SpringMVC的前端控制器(DispatcherServlet)统一交由全局异常处理器进行异常处理。
1、HandlerExceptionResolver接口
在SpringMVC中提供的HandlerExceptionResolver接口可以实现全局异常处理器,该接口的源码如下:
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.lang.Nullable;
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
可以看到,HandlerExceptionResolver接口中定义了一个名为resolveException的方法,该方法主要用于处理Controller中的异常。参数“Exception ex”即为Controller或其下层抛出的异常。参数“Object handler”就是处理器适配器要执行的Handler对象。resolveException方法的返回值类型是ModelAndView,也就是说,可以通过这个返回值来设置发送异常时的显示页面。
2、综合实例
【实例】在SpringMVC项目中,使用使用HandlerExceptionResolver实现全局异常处理器。当抛出异常后,要有相应的符合用户体验的友好界面显示异常。
(1)创建自定义异常类的名称为OperationException,定义的代码如下:
package com.pjb.fms.exception;
/**
* 自定义操作异常
* 注意:如果继承的是Exception类,那么Spring的事务管理将会失效,
* 只有继承RuntimeException类才使Spring的事务管理不会失效
* @author pan_junbiao
**/
public class OperationException extends RuntimeException
{
private String errorMessage; //异常信息
public OperationException(String errorMessage)
{
super(errorMessage);
this.errorMessage = errorMessage;
}
public String getErrorMessage()
{
return errorMessage;
}
public void setErrorMessage(String errorMessage)
{
this.errorMessage = errorMessage;
}
}
注意:该自定义异常类继承的是RuntimeException类,因为一般项目的Service层逻辑都会使用Spring提供的事务管理,当Service层需要抛出自定义异常时,如果该自定义异常继承的是Exception类,则Spring提供的事务管理将会失效,所以这里的自定义异常类继承的是RuntimeException类,这样才不会使Spring提供的事务管理失效。
Spring管理的事务,无论是声明式事务还是注解式事务默认是在抛出运行异常(RuntimeException异常)时,才会被Spring框架捕获到然后回滚。
(2)创建全局异常处理器OperationExceptionResolver类,该类继承HandlerExceptionResolver接口,并且实现resolveException方法,代码如下:
package com.pjb.fms.exception;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 全局异常处理器
* @author pan_junbiao
**/
public class OperationExceptionResolver implements HandlerExceptionResolver
{
//日志对象
private Logger logger = LogManager.getLogger(OperationExceptionResolver.class);
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
{
//1、解析出异常类型
OperationException operationException = null;
if(ex instanceof OperationException)
{
//2、如果该异常类型是自定义的操作异常,直接获取异常信息,在错误页面展示
operationException = (OperationException)ex;
}
else
{
//3、如果该异常类型不是自定义的操作异常,构建一个自定义的操作异常类型
operationException = new OperationException("系统错误,请联系管理员");
}
//错误信息
String errorMessage = operationException.getErrorMessage();
//记录异常日志
logger.error(errorMessage);
//返回错误页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("errorMessage",errorMessage);
modelAndView.setViewName("/error.jsp");
return modelAndView;
}
}
(3)创建名称为error.jsp的页面,符合用户体验的友好界面,用于显示异常信息。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>操作失败</title>
<meta name="author" content="pan_junbiao的博客">
</head>
<body>
<div align="center">
<h1>操作失败</h1>
<span style="color: red;">
<%=request.getAttribute("errorMessage") == null ? "" : "失败原因:" + request.getAttribute("errorMessage")%>
</span>
</div>
</body>
</html>
(4)然后在SpringMVC核心配置文件springmvc.xml中将定义的全局异常处理器配置进去:
<!-- 全局异常处理器 -->
<bean class="com.pjb.fms.exception.OperationExceptionResolver"></bean>
(5)创建测试的Controller控制器方法,该方法抛出一个自定义异常。
/**
* 用户登录
* @author pan_junbiao
*/
@RequestMapping(value = "login", method = RequestMethod.POST)
public ModelAndView login() throws Exception
{
throw new OperationException("账号或密码错误");
}
执行结果: