系统的异常处理一直是我们在开发过程中的一个痛点,今天就为大家介绍一下如何在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;
}
}
在这段示例中,添加了一个用于处理全局异常的默认处理方法defaultErrorHandler
。ExceptionHandler(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);
}
}
到这里自定义的异常处理器就配置完成了,大家可以尽情的测试吧.
注:上述方案无法并用,根据个人喜好进行选择