美文网首页
Spring 统一异常捕获处理之HandlerException

Spring 统一异常捕获处理之HandlerException

作者: return997 | 来源:发表于2019-04-10 20:22 被阅读0次

      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(转载)

    相关文章

      网友评论

          本文标题:Spring 统一异常捕获处理之HandlerException

          本文链接:https://www.haomeiwen.com/subject/xzooiqtx.html