聊聊 springmvc 异常处理

Posted tanyunlong_nice

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊聊 springmvc 异常处理相关的知识,希望对你有一定的参考价值。

     在做WEB项目中,无论是接口服务还是前后分离的web项目,对于RunTimeException异常和业务异常的处理是很重要的一部分,若不处理,业务异常抛出会给用户不友好的访问体验,DAO层的异常会暴露数据库的一些信息从而使得服务不安全等等,幸运的是JAVA依赖于强大的spring生态,springmvc 已经为我们准备了成熟的解决方案。

     springMVC提供的异常处理主要有两种方式,一种是直接实现自己的HandlerExceptionResolver,当然这也包括使用Spring已经为我们提供好的SimpleMappingExceptionResolver和DefaultHandlerExceptionResolver,另一种是使用注解的方式实现一个专门用于处理异常的Controller——ExceptionHandler。

一、HandlerExceptionResolver接口

    HandlerExceptionResolver是一个接口,springMVC本身已经对其有了一个自身的实现——DefaultHandlerExceptionResolver,该解析器只是对其中的一些比较典型的异常进行了拦截,然后返回对应的错误码,当然你也可以继承DefaultHandlerExceptionResolver类,然后重写其中的一些异常处理方法来实现自己的异常处理。

下面通过两张图来对比有无全局异常时,springmvc的工作流程:



 




代码实现:

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJacksonJsonView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 全局异常处理
 * Created by  on 2018/1/16.
 */
@Slf4j
@Component
public class ExceptionResolver implements HandlerExceptionResolver 

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) 

        log.error(" Exception",httpServletRequest.getRequestURI(),e);
        //jackjson 版本2.0+ 时候 参数使用mappingjackjson 2
        ModelAndView modelAndView = new ModelAndView(new MappingJacksonJsonView());
        modelAndView.addObject("status",ResponseCode.ERROR.getCode());
        modelAndView.addObject("msg","接口异常,详情请查看服务端日志接口端的信息");
        modelAndView.addObject("data",e.toString());
        return modelAndView;
    
二、使用@ ExceptionHandler

该注解用来统一处理某一类异常,从而能够减少代码重复率和复杂度

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExceptionHandler 

	/**
	 * Exceptions handled by the annotated method. If empty, will default to any
	 * exceptions listed in the method argument list.
	 */
	Class<? extends Throwable>[] value() default ;

首先我们来看一下注解的源码,他是作用域在方法上的运行时的注解,其参数的泛型为继承Throwable的Class

由该注解注释的方法可以具有灵活的输入参数(详细参见Spring API):
异常参数:包括一般的异常或特定的异常(即自定义异常),如果注解没有指定异常类,会默认进行映射。
请求或响应对象 (Servlet API or Portlet API): 你可以选择不同的类型,如ServletRequest/HttpServletRequest或PortleRequest/ActionRequest/RenderRequest。
Session对象(Servlet API or Portlet API): HttpSession或PortletSession。
WebRequest或NativeWebRequest 
Locale
InputStream/Reader 
OutputStream/Writer 
Model
方法返回值可以为:
ModelAndView对象
Model对象
Map对象
View对象
String对象
还有@ResponseBody、HttpEntity<?>或ResponseEntity<?>,以及void

因此我们可以写一个测试的demo:

import org.springframework.stereotype.Controller;  
import org.springframework.web.bind.annotation.ExceptionHandler;  
import org.springframework.web.bind.annotation.RequestMapping;  
  
import com.tiantian.blog.web.servlet.MyException;  
  
@Controller  
public class GlobalController   
  
      
    /** 
     * 用于处理异常的 
     * @return 
     */  
    @ExceptionHandler(MyException.class)  
    public String exception(MyException e)   
        System.out.println(e.getMessage());  
        e.printStackTrace();  
        return "exception";  
      
      
    @RequestMapping("test")  
    public void test()   
        throw new MyException("出错了!");  
      
      
      
  
但是使用此注解有一个很大的缺点:

使用@ExceptionHandler进行处理有一个不好的地方是进行异常处理的方法必须与出错的方法在同一个Controller里面

此时我们可以引用一个新的注解解决此问题:@ControllerAdvice

@ControllerAdvice,是spring3.2提供的新注解,从名字上可以看出大体意思是控制器增强。让我们先看看@ControllerAdvice的实现:

@Target(ElementType.TYPE)  
@Retention(RetentionPolicy.RUNTIME)  
@Documented  
@Component  
public @interface ControllerAdvice   
  
  
 没什么特别之处,需要注意的是该注解使用@Component注解,这样的话当我们使用<context:component-scan>扫描时也能扫描到
/**
* Indicates the annotated class assists a "Controller".
*
* <p>Serves as a specialization of @link Component @Component, allowing for
* implementation classes to be autodetected through classpath scanning.
*
* <p>It is typically used to define @link ExceptionHandler @ExceptionHandler,
* @link InitBinder @InitBinder, and @link ModelAttribute @ModelAttribute
* methods that apply to all @link RequestMapping @RequestMapping methods.
*
* @author Rossen Stoyanchev
* @since 3.2
*/
即把@ControllerAdvice注解内部使用@ExceptionHandler、@InitBinder、@ModelAttribute注解的方法应用到所有的 @RequestMapping注解的方法。非常简单,不过只有当使用@ExceptionHandler最有用,另外两个用处不大。

最终我们编写了一个比较完整的异常拦截处理类:

@ControllerAdvice
public class BaseController 

    private static final Logger logger = LoggerFactory.getLogger(BaseController.class);

    @ResponseBody
    @ExceptionHandler(BaseBusinessException.class)
    public String BusinessExceptionHandler(BaseBusinessException e) 
        return new Response<>().fail().errCode(e.getCode()).msg(e.getMessage()).toString();
    

    @ResponseBody
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public String MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) 
        return new Response<>().fail().errCode(BaseErrorCode.PARAM_ERROR.getCode()).msg(e.getMessage()).toString();
    

    @ResponseBody
    @ExceptionHandler(Exception.class)
    public String exceptionHandler(Exception e) 
        logger.error("全局异常: ", e.getMessage(), e);
        return new Response<>().fail().errCode(BaseErrorCode.SYSTEM_ERROR.getCode()).msg(e.getMessage()).toString();
    






以上是关于聊聊 springmvc 异常处理的主要内容,如果未能解决你的问题,请参考以下文章

SpringMVC统一异常处理

SpringMVC处理异常的三种方式

统一异常处理

SpringMVC -- SpringMVC异常处理机制

springmvc学习笔记(16)-异常处理器

SpringMVC(十四):SpringMVC异常处理