SpringBoot全局异常处理

系统的异常处理一直是我们在开发过程中的一个痛点,今天就为大家介绍一下如何在SpringBoot中进行异常处理,希望对大家有所帮助。

方案一:ControllerAdvice
新建一个用于异常处理的类,为其添加ControllerAdvice注解,然后配合ExceptionHandler注解添加异常处理方法。
举个栗子:

package com.jianggujin.hr;

import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;

@ControllerAdvice
public class GlobalExceptionHandler
{
   @ExceptionHandler(value = Exception.class)
   public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e)
         throws Exception
   {
      return createModelAndView(request, "500", HttpStatus.INTERNAL_SERVER_ERROR, e);
   }

   private ModelAndView createModelAndView(HttpServletRequest request, String viewName, HttpStatus status, Exception e)
   {
      ModelAndView mav = new ModelAndView();
      if (e != null)
      {
         mav.addObject("error", e);
      }
      mav.addObject("url", request.getRequestURI());
      mav.addObject("method", request.getMethod());
      if (status != null)
      {
         mav.setStatus(status);
      }
      mav.setViewName(viewName);
      return mav;
   }
}

在这段示例中,添加了一个用于处理全局异常的默认处理方法defaultErrorHandlerExceptionHandler(value = Exception.class)表示用于处理Exception类型的异常,同理,如果我们要处理其他类型的异常,我们只需要更改ExceptionHandler注解中的value对应的异常类型,可以为多个。

随便写个控制器测试一下:

package com.jianggujin.hr.controller.api;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestApiController
{
   @RequestMapping({ "/test" })
   public Object test()
   {
      throw new RuntimeException("ddd");
   }
}

访问这个测试地址,会发现页面跳转到了我们自定义的异常页面。
这里写图片描述

虽然我们的错误页面已经显示了,但是不知道各位有没有发现一个问题,在控制器中,我使用的注解是RestController,意味着该类下面的方法都是rest风格的API,所以出现错误之后,不应该显示错误页面,而是应该响应json之类的错误数据(以实际情况为准)。这时候我们可以返回一个null的ModelAndView,并且为方法添加一个HttpServletResponse对象,在该方法中,可以直接输出响应内容。

@ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) throws Exception
{
   response.setContentType("application/json;charset=utf-8");
   PrintWriter writer = response.getWriter();
   writer.write("{error: 1}");
   writer.flush();
   return null;
}

这里写图片描述

这样,错误响应信息就改变了。但是到这里,依然存在问题。我想实现根据不同的请求响应不同错误信息,如何实现呢?首先我们需要区分出哪些是页面的请求,哪些是api的请求,这两者最大的区别就是注解不同,如果是api的请求,那么在方法上面会有ResponseBody注解,或者控制器上面会有RestController注解,根据这两个特征,我们可以很容易的进行区分,我们只需要对异常处理方法进行如下改造。

@ExceptionHandler(value = Exception.class)
   public ModelAndView defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception e,
         HandlerMethod method) throws Exception
{
   ModelAndView modelAndView = null;
   boolean isApi = method != null && (method.hasMethodAnnotation(ResponseBody.class)
            || method.getBeanType().isAnnotationPresent(RestController.class));
   if (isApi)
   {
      response.setContentType("application/json;charset=utf-8");
      PrintWriter writer = response.getWriter();
      writer.write("{error: 1}");
      writer.flush();
   }
   else
   {
      modelAndView = createModelAndView(request, "500", HttpStatus.INTERNAL_SERVER_ERROR, e);
   }
   return modelAndView;
}

首先我们判断HandlerMethod是否为API,如果是则对其进行特殊处理,这样就可以实现普通页面与API请求的不同响应了。

方案二:HandlerExceptionResolver
通过HandlerExceptionResolver实现我们自己的全局异常处理。这是比较原始的实现方式。我的实现方式是这样的,首先编写一个抽象类实现HandlerExceptionResolver接口。

package com.jianggujin.hr.util;

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

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;

/**
 * 异常解析
 * 
 * @author jianggujin
 *
 */
public abstract class AbstractHandlerExceptionResolver implements HandlerExceptionResolver
{
   protected static final Log logger = LogFactory.getLog(AbstractHandlerExceptionResolver.class);

   @Override
   public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
         Exception e)
   {
      ModelAndView modelView = null;
      try
      {
         onException(e);
      }
      catch (Exception e1)
      {
         logger.error("ExceptionResolver onException", e1);
      }
      // 处理器方法,才会执行异常处理
      if (handler instanceof HandlerMethod)
      {
         HandlerMethod method = (HandlerMethod) handler;

         boolean isApi = (method.hasMethodAnnotation(ResponseBody.class)
               || method.getBeanType().isAnnotationPresent(RestController.class));
         try
         {
            // api
            if (isApi)
            {
               modelView = onApiError(request, response, e, method);
            }
            else
            {
               modelView = onPageError(request, response, e, method);
            }
         }
         catch (Exception e1)
         {
            logger.error("ExceptionResolver onError", e1);
         }
      }
      return modelView;
   }

   /**
    * 出现异常时回调
    * 
    */
   protected abstract void onException(Exception e) throws Exception;

   /**
    * api请求出错
    * 
    */
   protected abstract ModelAndView onApiError(HttpServletRequest request,
         HttpServletResponse response, Exception e, HandlerMethod method) throws Exception;

   /**
    * 页面请求出错
    * 
    */
   protected abstract ModelAndView onPageError(HttpServletRequest request,
         HttpServletResponse response, Exception e, HandlerMethod method) throws Exception;
}

在我们自己编写的AbstractHandlerExceptionResolver中提供了三个回调方法用于按照实际需求进行重写实现,大家可以自由发挥,最后我们只需要配置异常处理器就好了。

package com.jianggujin.hr;

import java.util.List;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

import com.jianggujin.hr.util.ExceptionResolver;
import com.jianggujin.hr.util.ExceptionResolverListener;

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter
{
   @Override
   public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers)
   {
      ExceptionResolverHandler resolverHandler = new ExceptionResolverHandler();
      exceptionResolvers.add(resolverHandler);
   }
}

到这里自定义的异常处理器就配置完成了,大家可以尽情的测试吧.

注:上述方案无法并用,根据个人喜好进行选择

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值