Spring MVC异常处理
本小记学习目标
-
SimpleMappingExceptionResolver
-
HandlerExceptionResolver接口
-
@ExceptionHandler注解
Spring MVC开发中,不管是对数据库操作、业务层操作、控制器层操作都有可能产生各种异常需要处理,对于异常的处理如果散落在各处,则它的耦合性则太高,从而加大了工作量并且处理方式不好统一。Spring MVC框架为了解决这个问题提供了相应的解决方案。
我们有三种式方式来统一处理异常:
第一、Spirng MVC提供的简单异常处理器SimpleMappingExceptionResolver
第二、Spring的异常处理接口HandlerExceptionResolver接口
第三、@ExceptionHandler注解
一、SimpleMappingExceptionResolver
实例说明
1.新增一个Maven的war工程,在pom.xml中导入如下jar依赖
<
dependencies
>
<!-- context -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-context
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- Spring MVC -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-
webmvc
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- Spring web -->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-web
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<!-- commons-logging -->
<
dependency
>
<
groupId
>commons-logging
</
groupId
>
<
artifactId
>commons-logging
</
artifactId
>
<
version
>1.2
</
version
>
</
dependency
>
<!-- spring-
aop
-->
<
dependency
>
<
groupId
>org.springframework
</
groupId
>
<
artifactId
>spring-
aop
</
artifactId
>
<
version
>5.0.2.RELEASE
</
version
>
</
dependency
>
<
dependency
>
<
groupId
>javax.servlet
</
groupId
>
<
artifactId
>
jstl
</
artifactId
>
<
version
>1.2
</
version
>
</
dependency
>
</
dependencies
>
2.新增一个自己定义的异常类com.xiaoxie.exception.MyException
package com.xiaoxie.exception;
public
class MyException
extends Exception{
private
static
final
long
serialVersionUID = 1L;
public MyException() {
super();
}
public MyException(String
message) {
super(
message);
}
}
3.新增一个Dao,com.xiaoxie.dao.ExceptionDao
package com.xiaoxie.dao;
import java.sql.SQLException;
import org.springframework.stereotype.Repository;
import com.xiaoxie.exception.MyException;
@Repository(
"exceptionDao")
public
class ExceptionDao {
public
void daoDBException()
throws SQLException {
throw
new SQLException(
"Dao中数据库异常!");
}
public
void daoMyException()
throws MyException {
throw
new MyException(
"Dao中自定义的异常!");
}
public
void daoUnknownException()
throws Exception {
throw
new Exception(
"Dao中未知异常!");
}
}
4.新增一个service接口和service的实现类,com.xiaoxie.service.ExceptionService、com.xiaoxie.service.impl.ExceptionServiceImpl
ExceptionService:
package com.xiaoxie.service;
public
interface ExceptionService {
void serviceMyException()
throws Exception;
void serviceDBException()
throws Exception;
void serviceUnknownException()
throws Exception;
void daoMyException()
throws Exception;
void daoDBException()
throws Exception;
void daoUnknownException()
throws Exception;
}
ExceptionServiceImpl:
package com.xiaoxie.service.impl;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.xiaoxie.dao.ExceptionDao;
import com.xiaoxie.exception.MyException;
import com.xiaoxie.service.ExceptionService;
@Service(
"exceptionService")
public
class ExceptionServiceImpl
implements ExceptionService{
@Autowired
private ExceptionDao
exceptionDao;
@Override
public
void serviceMyException()
throws Exception {
throw
new MyException(
"service中自己定义异常!");
}
@Override
public
void serviceDBException()
throws Exception {
throw
new SQLException(
"service中数据库异常!");
}
@Override
public
void serviceUnknownException()
throws Exception {
throw
new Exception(
"service中未知异常!");
}
@Override
public
void daoMyException()
throws Exception {
exceptionDao.daoMyException();
}
@Override
public
void daoDBException()
throws Exception {
exceptionDao.daoDBException();
}
@Override
public
void daoUnknownException()
throws Exception {
exceptionDao.daoUnknownException();
}
}
5.新增Controller类:com.xiaoxie.controller.ExceptionController
package com.xiaoxie.controller;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.xiaoxie.exception.MyException;
import com.xiaoxie.service.ExceptionService;
@Controller
public
class ExceptionController {
@Autowired
private ExceptionService
exceptionService;
@RequestMapping(
"/db")
public
void controllerDBExcepton()
throws Exception {
throw
new SQLException(
"控制器中数据库异常!");
}
@RequestMapping(
"/my")
public
void controllerMyException()
throws Exception {
throw
new MyException(
"控制器中自定义异常!");
}
@RequestMapping(
"/no")
public
void controllerUnknownException()
throws Exception {
throw
new Exception(
"控制器中未知异常!");
}
@RequestMapping(
"/servicedb")
public
void serviceDBExcepton()
throws Exception {
exceptionService.serviceDBException();
}
@RequestMapping(
"/servicemy")
public
void serviceMyException()
throws Exception {
exceptionService.serviceMyException();
}
@RequestMapping(
"/serviceno")
public
void serviceUnknownException()
throws Exception {
exceptionService.serviceUnknownException();
}
@RequestMapping(
"/daodb")
public
void daoDBExcepton()
throws Exception {
exceptionService.daoDBException();
}
@RequestMapping(
"/daomy")
public
void daoMyException()
throws Exception {
exceptionService.daoMyException();
}
@RequestMapping(
"/daono")
public
void daoUnknownException()
throws Exception {
exceptionService.daoUnknownException();
}
}
6.定义视图:index.jsp,404.jsp,error.jsp,my-error.jsp,sql-error.jsp(注意:这里的视图都是定义在webcontent下的,这里的位置与配置视图解析器时有关系)
index.jsp:
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>index
</
title
>
</
head
>
<
body
>
<
a
href=
"${pageContext.request.contextPath }
/daodb"
>处理
dao中数据库异常
</
a
>
<
a
href=
"${pageContext.request.contextPath }
/daomy"
>处理
dao中自定义异常
</
a
>
<
a
href=
"${pageContext.request.contextPath }
/daono"
>处理
dao中未知异常
</
a
>
<
hr
/>
<
a
href=
"${pageContext.request.contextPath }
/servicedb"
>处理service中数据库异常
</
a
>
<
a
href=
"${pageContext.request.contextPath }
/servicemy"
>处理service中自定义异常
</
a
>
<
a
href=
"${pageContext.request.contextPath }
/serviceno"
>处理service中未知异常
</
a
>
<
hr
/>
<
a
href=
"${pageContext.request.contextPath }
/db"
>处理controller中数据库异常
</
a
>
<
a
href=
"${pageContext.request.contextPath }
/my"
>处理controller中自定义异常
</
a
>
<
a
href=
"${pageContext.request.contextPath }
/no"
>处理controller中未知异常
</
a
>
<
hr
/>
<
a
href=
"${pageContext.request.contextPath }
/404"
>404错误
</
a
>
</
body
>
</
html
>
404.jsp:
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
isErrorPage=
"true"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>404
</
title
>
</
head
>
<
body
>
资源不存在!
</
body
>
</
html
>
error.jsp:
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
isErrorPage=
"true"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>未知异常
</
title
>
</
head
>
<
body
>
程序出现未知错误:
<%=exception
%>
<
br
/>
错误内容:
<
br
/>
<%
exception.printStackTrace(response.getWriter());
%>
</
body
>
</
html
>
my-error.jsp:
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
isErrorPage=
"true"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>自定义异常
</
title
>
</
head
>
<
body
>
自定义异常错误:
<%=exception
%>
<
br
/>
错误内容:
<
br
/>
<%
exception.printStackTrace(response.getWriter());
%>
</
body
>
</
html
>
sql-error.jsp:
<%@
page
language=
"java"
contentType=
"text/html; charset=UTF-8"
pageEncoding=
"UTF-8"
isErrorPage=
"true"
%>
<!
DOCTYPE
html
PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd"
>
<
html
>
<
head
>
<
meta
http-equiv=
"Content-Type"
content=
"text/html; charset=UTF-8"
>
<
title
>Insert title here
</
title
>
</
head
>
<
body
>
数据库异常错误:
<%=exception
%>
<
br
/>
错误内容:
<
br
/>
<%
exception.printStackTrace(response.getWriter());
%>
</
body
>
</
html
>
注意:几个异常jsp页面中定义了isErrorPage="true",否则不可以使用exception对象
7.web.xml配置
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<
web-app
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns=
"http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation=
"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id=
"WebApp_ID"
version=
"2.5"
>
<!-- 全局的异常处理页面 -->
<
error-page
>
<
error-code
>404
</
error-code
>
<
location
>/404.
jsp
</
location
>
</
error-page
>
<!-- 前端核心
servlet
的配置(DispatcherServlet) -->
<
servlet
>
<
servlet-name
>springDispatcherServlet
</
servlet-name
>
<
servlet-class
>org.springframework.web.servlet.DispatcherServlet
</
servlet-class
>
<
init-param
>
<
param-name
>contextConfigLocation
</
param-name
>
<
param-value
>classpath:springmvc.xml
</
param-value
>
</
init-param
>
<
load-on-startup
>1
</
load-on-startup
>
</
servlet
>
<!-- Map all requests to the DispatcherServlet for handling -->
<
servlet-mapping
>
<
servlet-name
>springDispatcherServlet
</
servlet-name
>
<
url-pattern
>/
</
url-pattern
>
</
servlet-mapping
>
<!-- 配置解决
psot
请求中文乱码问题 -->
<
filter
>
<
filter-name
>CharacterEncodingFilter
</
filter-name
>
<
filter-class
>org.springframework.web.filter.CharacterEncodingFilter
</
filter-class
>
<
init-param
>
<
param-name
>encoding
</
param-name
>
<
param-value
>
utf-8
</
param-value
>
</
init-param
>
<
init-param
>
<
param-name
>forceEncoding
</
param-name
>
<
param-value
>true
</
param-value
>
</
init-param
>
</
filter
>
<
filter-mapping
>
<
filter-name
>CharacterEncodingFilter
</
filter-name
>
<
url-pattern
>/*
</
url-pattern
>
</
filter-mapping
>
</
web-app
>
注意:404的配置,当出现404的错误时定位到404.jsp,由于404.jsp这个页面是在webcontent目录下,所以配置时使用/404.jsp
8.在classpath下新增spring的配置文件springmvc.xml
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
xmlns:context=
"http://www.springframework.org/schema/context"
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-4.3.xsd"
>
<!-- spring自动扫描 -->
<
context:component-scan
base-package=
"com.xiaoxie.controller"
/>
<
context:component-scan
base-package=
"com.xiaoxie.service"
/>
<
context:component-scan
base-package=
"com.xiaoxie.dao"
/>
<!-- 配置视图解析器 -->
<
bean
class=
"org.springframework.web.servlet.view.InternalResourceViewResolver"
id=
"internalResourceViewResolver"
>
<!-- 前缀 -->
<
property
name=
"
prefix"
value=
"/"
/>
<!-- 后缀 -->
<
property
name=
"
suffix"
value=
".jsp"
/>
</
bean
>
<!-- 配置SimpleMappingExceptionResolver(异常与视图的对应关系) -->
<
bean
class=
"org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"
id=
"simpleMappingExceptionResolver"
>
<!-- 定义默认的异常处理页面 -->
<
property
name=
"
defaultErrorView"
value=
"error"
/>
<!-- 定义异常处理页面用来获取异常信息的变量名称 ,默认为exception -->
<
property
name=
"
exceptionAttribute"
value=
"ex"
/>
<!-- 定义需要特殊处理的异常 -->
<
property
name=
"
exceptionMappings"
>
<
props
>
<
prop
key=
"com.xiaoxie.exception.MyException"
>my-error
</
prop
>
<
prop
key=
"java.sql.SQLException"
>
sql-error
</
prop
>
</
props
>
</
property
>
</
bean
>
</
beans
>
注意:这个配置文件中的关键是配置了SimpleMappingExceptionResolver这个bean,从与把产生的异常与对应的视图关联起来
二、HandlerExceptionResolver接口实现异常的统一处理
org.springframework.web.servlet.HandlerExceptionResolver接口用于解析请求处理过程中所产生的异常,我们可以实现这个接口来对异常做统一的处理。
1.在com.xiaoxie.exception包下新增一个实现类MyExceptionHandler
package com.xiaoxie.exception;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
public
class MyExceptionHandler
implements HandlerExceptionResolver{
@Override
public ModelAndView resolveException(HttpServletRequest
request, HttpServletResponse
response, Object
handler,
Exception
ex) {
Map<String,Object>
model =
new HashMap<String,Object>();
model.put(
"ex",
ex);
//根据不同的异常来转到不同的页面
if(
ex
instanceof MyException) {
return
new ModelAndView(
"my-error",
model);
}
else
if(
ex
instanceof SQLException) {
return
new ModelAndView(
"sql-error",
model);
}
else {
return
new ModelAndView(
"error",
model);
}
}
}
2.在Spring的配置文件中托管MyExceptionHandler,对实例中的SimpleMappingExceptionResolver进行注释,并在后面添加如下配置进行MyExceptionHandler的托管
<!-- Spring托管MyExceptionHandler -->
<
bean
class=
"com.xiaoxie.exception.MyExceptionHandler"
/>
三、@ExceptionHandler注解声明异常处理方法
1.定义一个抽象类:BaseExceptionController类
package com.xiaoxie.controller;
import java.sql.SQLException;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ExceptionHandler;
import com.xiaoxie.exception.MyException;
public
abstract
class BaseExceptoinController {
@ExceptionHandler
public String exception(HttpServletRequest
request,Exception
ex) {
//request.setAttribute("
ex
",
ex
);
if(
ex
instanceof MyException) {
return
"my-error";
}
else
if(
ex
instanceof SQLException) {
return
"sql-error";
}
else {
return
"error";
}
}
}
2.对于controller实现类extends BaseExceptionController
3.把实例中对MyExceptionHandler托管的配置进行注释