spring的异常统一处理非常简单,首先我们需要看一下Spring中定义的HandlerExceptionResolver接口
/**
* Interface to be implemented by objects than can resolve exceptions thrown
* during handler mapping or execution, in the typical case to error views.
* Implementors are typically registered as beans in the application context.
*
* <p>Error views are analogous to the error page JSPs, but can be used with
* any kind of exception including any checked exception, with potentially
* fine-granular mappings for specific handlers.
*
* @author Juergen Hoeller
* @since 22.11.2003
*/
public interface HandlerExceptionResolver {
/**
* Try to resolve the given exception that got thrown during on handler execution,
* returning a ModelAndView that represents a specific error page if appropriate.
* <p>The returned ModelAndView may be {@linkplain ModelAndView#isEmpty() empty}
* to indicate that the exception has been resolved successfully but that no view
* should be rendered, for instance by setting a status code.
* @param request current HTTP request
* @param response current HTTP response
* @param handler the executed handler, or <code>null</code> if none chosen at the
* time of the exception (for example, if multipart resolution failed)
* @param ex the exception that got thrown during handler execution
* @return a corresponding ModelAndView to forward to,
* or <code>null</code> for default processing
*/
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
它定义了一个resolveException方法,我们如果需要处理异常的话,需要实现这个接口,并且实现resolveException方法,在resolveException方法中处理自己的业务逻辑。
pom导入setvlet依赖
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>jsp-api</artifactId>
<version>6.0.36</version>
</dependency>
假如我设计一个自定义的异常处理类:
package com.permission.common;
import com.permission.exception.PermissionException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @version:1.0.0
* @author: lironghong
* @date: 2019/4/10 23:07
* @description: 统一捕获异常处理
* 当我们的定义的resolver实现HandlerExceptionResolver后这个类被spring管理的话我们的全局异常在http进行返回的时候就会被这个类所捕捉住
* 我们只需要在resolveException这个方法中实现我们异常处理逻辑就可以了
*/
@Slf4j
public class SpringExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception e) {
String url = httpServletRequest.getRequestURI().toString();
/*
* 该对象中包含了一个model属性和一个view属性
* model:其实是一个ModelMap类型。其实ModelMap是一个LinkedHashMap的子类
* view:包含了一些视图信息
* 当视图解释器解析ModelAndVIew是,其中model本生就是一个Map的实现类的子类。视图解析器将model中的每个元素都通过request.setAttribute(name, value);添加request请求域中。这样就可以在JSP页面中通过EL表达式来获取对应的值*/
ModelAndView mv = null;
String defaultMsg = "System error";
//数据请求使用 .json 页面请求使用 .page
if (url.endsWith(".json")) {
/*java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。*/
if (e instanceof PermissionException) {
JsonData jsonData = JsonData.failed(e.getMessage());
//jsonView对应spring-servlet.xml中的jsonView配合(意思是返回值是用json返回的)
mv = new ModelAndView("jsonView", jsonData.tomap());
} else {
log.error("{},{}","unknow json exception url:"+url,e);
JsonData jsonData = JsonData.failed(defaultMsg);
mv = new ModelAndView("jsonView", jsonData.tomap());
}
} else if (url.endsWith(".page")) {
log.error("{},{}","unknow page exception url:"+url,e);
JsonData jsondata = JsonData.failed(defaultMsg);
//页面请求异常会找exception.jsp页面
mv = new ModelAndView("exception", jsondata.tomap());
} else {
log.error("{},{}","unknow exception url:"+url,e);
JsonData jsonData = JsonData.failed(defaultMsg);
mv = new ModelAndView("jsonView", jsonData.tomap());
}
return mv;
}
}
然后需要将我们自定义的Resolver类注入到bean中
<bean id="MyExceptionResolver" class="com.tiantian.xxx.web.handler.MyExceptionResolver"/>
测试
import com.permission.common.JsonData;
import com.permission.exception.PermissionException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/test")
@Slf4j
public class TestController {
@RequestMapping("/hello.json")
@ResponseBody
public JsonData hello(){
log.info("hello");
throw new PermissionException("test exception");
// return JsonData.success("hello,permission");
}
}
JsonDate统一json返回组件
package com.permission.common;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* @version:1.0.0
* @author: lironghong
* @date: 2019/4/2 17:56
* @description: 统一json返回组件
*/
@Data
public class JsonData {
private boolean ret;
private String msg;
private Object data;
public JsonData (boolean ret){
this.ret=ret;
}
public static JsonData success(Object object,String msg){
JsonData jsonData = new JsonData(true);
jsonData.data=object;
jsonData.msg=msg;
return jsonData;
}
public static JsonData success(Object object){
JsonData jsonData = new JsonData(true);
jsonData.data=object;
return jsonData;
}
public static JsonData success(){
return new JsonData(true);
}
public static JsonData failed(String msg){
JsonData jsonData=new JsonData(false);
jsonData.msg=msg;
return jsonData;
}
//补充方法
public Map<String,Object> tomap(){
HashMap<String,Object> result=new HashMap<String,Object>();
result.put("ret",ret);
result.put("msg",msg);
result.put("data",data);
return result;
}
}
具体怎么调用的?我们可以看下spring中的doDispach方法中,有这么一段:
catch (ModelAndViewDefiningException ex) {
logger.debug("ModelAndViewDefiningException encountered", ex);
mv = ex.getModelAndView();
}
catch (Exception ex) {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(processedRequest, response, handler, ex);
errorView = (mv != null);
}
其中processHandlerException就是用来捕获异常处理的,那么继续看processHandlerException方法
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
这个方法中的handlerExceptionResolver.resolveException就是用来捕获异常的,并且Spring允许多个自定义的异常类实现。可以看this.handlerExceptionResolvers方法,跟踪进去
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
OrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, no HandlerExceptionResolver is fine too.
}
}
// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found.
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
}
}
}
可以清晰的看到这个方法是将handlerExceptionResolvers进行了初始化,并将自定义的异常处理类(可以多个)写入this.handlerExceptionResolvers(转载)
网友评论