SpringMVC框架08——统一异常处理
前言
在Spring MVC 应用的开发中,不管是对底层数据库操作,还是业务层或控制层操作,都会不可避免地遇到各种可预知的、不可预知的异常需要处理。如果每个过程都单独处理异常,那么系统的代码耦合度高,工作量大且不好统一,以后维护的工作量也很大。
在Spring MVC中提供了三种统一异常处理的方式,能够将所有类型的异常处理从各层中解耦出来,这样既保证了相关处理过程的功能单一,又实现了异常信息的统一处理和维护。
1、演示案例准备
为了验证Spring MVC 框架的3中异常处理方式,需要编写一个测试的应用,从Dao层、Service层、Controller层分别抛出不同的异常。本教程指定了3个异常,分别是:SQLException、自定义异常和未知异常,然后分别集成3种方式进行异常处理。
分别创建相应的包和类,如下图所示:
(1)创建自定义异常类MyException
代码示例:
package com.demo.exception; public class MyException extends Exception { public MyException() { } public MyException(String message) { super(message); } }
(2)创建Dao层异常类
代码示例:
package com.demo.dao; import com.demo.exception.MyException; import org.springframework.stereotype.Repository; import java.sql.SQLException; @Repository("testExceptionDao") public class TestExceptionDao { public void daodb() throws SQLException { throw new SQLException("Dao中数据库异常"); } public void daomy() throws MyException { throw new MyException("Dao中自定义异常"); } public void daono() throws Exception { throw new Exception("Dao中的未知异常"); } }
(3)创建Service层异常类
在service包下创建TestExceptionService接口和TestExceptionServiceImpl实现类,在该接口中定义6个方法,其中有3个是调用Dao层,有3个是Service层的方法。
TestExceptionService接口代码示例:
package com.demo.service; public interface TestExceptionService { public void servicedb() throws Exception; public void servicemy() throws Exception; public void serviceno() throws Exception; public void daodb() throws Exception; public void daomy() throws Exception; public void daono() throws Exception; }
TestExceptionServiceImpl实现类代码示例:
package com.demo.service; import com.demo.dao.TestExceptionDao; import com.demo.exception.MyException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.sql.SQLException; @Service("testExceptionService") public class TestExceptionServiceImpl implements TestExceptionService { @Autowired private TestExceptionDao testExceptionDao; @Override public void servicedb() throws Exception { throw new SQLException("Service中数据库异常"); } @Override public void servicemy() throws Exception { throw new MyException("Service中自定义异常"); } @Override public void serviceno() throws Exception { throw new Exception("Service中未知异常"); } @Override public void daodb() throws Exception { testExceptionDao.daodb(); } @Override public void daomy() throws Exception { testExceptionDao.daomy(); } @Override public void daono() throws Exception { testExceptionDao.daono(); } }
(4)创建控制器层异常类
代码示例:
package com.demo.controller; import com.demo.exception.MyException; import com.demo.service.TestExceptionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import java.sql.SQLException; @Controller public class TestExceptionController { @Autowired private TestExceptionService testExceptionService; @RequestMapping("/db") public void db() throws SQLException { throw new SQLException("控制器中数据库异常"); } @RequestMapping("/my") public void my() throws MyException { throw new MyException("控制器中自定义异常"); } @RequestMapping("no") public void no() throws Exception { throw new Exception("控制器中未知异常"); } @RequestMapping("/servicedb") public void servicedb() throws Exception { testExceptionService.servicedb(); } @RequestMapping("/servicemy") public void servicemy() throws Exception { testExceptionService.servicemy(); } @RequestMapping("/serviceno") public void serviceno() throws Exception { testExceptionService.serviceno(); } @RequestMapping("/daodb") public void daodb() throws Exception { testExceptionService.daodb(); } @RequestMapping("/daomy") public void daomy() throws Exception { testExceptionService.daomy(); } @RequestMapping("/daono") public void daono() throws Exception { testExceptionService.daono(); } }
(5)创建View视图层
View层共有5个JSP页面,分别是:
测试应用首页index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %> <html> <body> <h1>演示案例</h1> <p> <a href="${pageContext.request.contextPath}/daodb">处理dao中数据库异常</a> </p> <p> <a href="${pageContext.request.contextPath}/daomy">处理dao中自定义异常</a> </p> <p> <a href="${pageContext.request.contextPath}/daono">处理dao中未知异常</a> </p> <p> <a href="${pageContext.request.contextPath}/servicedb">处理service中数据库异常</a> </p> <p> <a href="${pageContext.request.contextPath}/servicemy">处理service中自定义异常</a> </p> <p> <a href="${pageContext.request.contextPath}/serviceno">处理service中未知异常</a> </p> <p> <a href="${pageContext.request.contextPath}/db">处理controller中数据库异常</a> </p> <p> <a href="${pageContext.request.contextPath}/my">处理controller中自定义异常</a> </p> <p> <a href="${pageContext.request.contextPath}/no">处理controller中未知异常</a> </p> </body> </html>
404错误对应页面404.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>404</title> </head> <body> <h2>资源不存在</h2> </body> </html>
未知异常对应页面error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %> <html> <head> <title>未知异常</title> </head> <body> <h1>未知错误:</h1> <%=exception%> <h2>错误内容:</h2> <% exception.printStackTrace(response.getWriter()); %> </body> </html>
自定义异常对应页面my-error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %> <html> <head> <title>自定义异常</title> </head> <body> <h1>自定义异常错误:</h1> <%=exception%> <h2>错误内容:</h2> <% exception.printStackTrace(response.getWriter()); %> </body> </html>
SQL异常对应页面sql-error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %> <html> <head> <title>数据库异常</title> </head> <body> <h1>数据库异常错误:</h1> <%=exception%> <h2>错误内容:</h2> <% exception.printStackTrace(response.getWriter()); %> </body> </html>
(6)配置全局异常处理
在web.xml中配置全局异常404处理
<!--配置全局异常--> <error-page> <error-code>404</error-code> <location>/404.jsp</location> </error-page>
2、使用配置统一处理异常
在springmvc.xml中配置org.springframework.web.servlet.handler.SimpleMappingExceptionResolver类,并且要提前配置异常类和View的对应关系。
springmvc.xml配置代码如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <!--将AnnotationHandler自动扫描到IOC容器中--> <context:component-scan base-package="com"></context:component-scan> <!--配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--配置前缀--> <property name="prefix" value="/"></property> <!--配置后缀--> <property name="suffix" value=".jsp"></property> </bean> <!--配置异常相关--> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <!--定义默认的异常处理页面,当该异常类型注册时使用--> <property name="defaultErrorView" value="error"></property> <!--定义异常处理页面用来获取异常信息的变量名,默认名为exception--> <property name="exceptionAttribute" value="ex"></property> <!--定义需要特殊处理的异常,用类名或完全路径名作为key,异常页名作为值--> <property name="exceptionMappings"> <props> <prop key="com.demo.exception.MyException">my-error</prop> <prop key="java.sql.SQLException">sql-error</prop> <!--在这里还可以继续扩展对不同异常类型的处理--> </props> </property> </bean> </beans>
演示效果:
404演示效果:
3、使用接口统一处理异常
org.springframework.web.servlet.HandlerExceptionResolver 接口用于解析请求处理过程中所产生的异常。在exception包中创建一个HandlerExceptionResolver接口的实现类MyExceptionHandler,代码如下:
package com.demo.exception; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; public class MyExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object obj, Exception e) { Map<String,Object> model = new HashMap<String,Object>(); model.put("ex",e); //根据不同错误转向不同页面(统一处理),即异常与View的对应关系 if (e instanceof MyException) { return new ModelAndView("my-error",model); }else if (e instanceof SQLException) { return new ModelAndView("sql-error",model); }else { return new ModelAndView("error",model); } } }
在springmvc.xml文件中配置实现类MyExceptionHandler的托管
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <!--将AnnotationHandler自动扫描到IOC容器中--> <context:component-scan base-package="com"></context:component-scan> <!--配置视图解析器--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--配置前缀--> <property name="prefix" value="/"></property> <!--配置后缀--> <property name="suffix" value=".jsp"></property> </bean> <!--托管MyExceptionHandler--> <bean class="com.demo.exception.MyExceptionHandler"></bean> </beans>
4、使用注解统一处理异常
在controller包下创建BaseController类,并在该类的方法中使用@ExceptionHandler注解声明异常处理方法,代码如下:
package com.demo.controller; import com.demo.exception.MyException; import org.springframework.web.bind.annotation.ExceptionHandler; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.sql.SQLException; public abstract class BaseController { @ExceptionHandler public String exception(HttpServletRequest request, HttpServletResponse response,Exception e){ request.setAttribute("ex",e); if (e instanceof MyException) { return "my-error"; } else if (e instanceof SQLException) { return "sql-error"; } else { return "error"; } } }
将所有需要异常处理的Controller都继承BaseController类,示例代码如下:
@Controller public class TestExceptionController extends BaseController{ //... }