> 文章列表 > 优雅的处理sping项目全局异常

优雅的处理sping项目全局异常

优雅的处理sping项目全局异常

全局异常处理

为了达到系统的各个模块中都能够共用同一个异常处理逻辑,避免代码重复和错误。在Spring框架中,可以通过全局异常处理来捕获应用程序中抛出的异常,并根据需要进行处理。

@ControllerAdvice

@ControllerAdvice是Spring MVC框架中的一个注解,用于定义一个全局的异常处理器和绑定响应数据的方法。

当Spring MVC中的Controller抛出异常时,它会查找@ControllerAdvice注解标注的类,然后调用其中匹配异常类型的方法来处理异常。这些方法可以返回一个ModelAndView对象、一个ResponseEntity对象或者其它任意类型的对象。此外,这些方法也可以通过@ExceptionHandler注解将响应数据绑定到请求中,使其可在视图中渲染。

@ControllerAdvice注解可以用于定义全局的异常处理器,以便在系统的各个模块中都能够共用同一个异常处理逻辑,避免代码重复和错误。例如,在处理Web应用程序的RESTful API时,可以使用@ControllerAdvice注解定义一个异常处理器,以便在出现异常时返回一个统一的错误响应格式。

@ControllerAdvice注解也可以用于定义全局的数据绑定方法,以便在处理请求时,自动将一些公共的响应数据绑定到请求中,避免了在Controller中的方法中的重复绑定。

@ControllerAdvice注解通常用于Web应用程序中,可以与@ExceptionHandler、@InitBinder和@ModelAttribute注解一起使用。

定义全局异常处理类

定义一个全局异常处理类,通过@ControllerAdvice注解,其中定义了匹配异常类型的方法来处理异常,用注册@ExceptionHandler({Throwable.class})注解方法,Throwable.class为匹配的异常类型。

/* 全局异常. @author <a href="mailto:felix@gmail.com">lht</a>* @date 2023/4/17 16:27* @since 1.0.0/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {@Autowiredprivate MessageSource messageSource;/* Throwable异常处理. @param e       e* @param request request* @return ResponseEntity*/@ExceptionHandler({Throwable.class})public ResponseEntity<R> handleGlobalExceptionResponse(final Throwable e, final HttpServletRequest request) {log.error("handleGlobalException: ", e);return ExceptionHandlerUtil.handleGlobalException(this.messageSource);}/* BusinessException异常处理. @param businessException businessException* @param request           request* @return ResponseEntity*/@ExceptionHandler({BusinessException.class})public ResponseEntity<R> handleServiceExceptionResponse(final BusinessException businessException, final HttpServletRequest request) {return ExceptionHandlerUtil.handleBusinessException(this.messageSource, businessException);}/* MethodArgumentNotValidException异常处理. @param e       e* @param request request* @return result*/@ExceptionHandler({MethodArgumentNotValidException.class})public ResponseEntity<R> handleMethodArgumentNotValidException(final MethodArgumentNotValidException e, final HttpServletRequest request) {String key = ((ObjectError) e.getBindingResult().getAllErrors().get(0)).getDefaultMessage();return ExceptionHandlerUtil.handleBusinessException(this.messageSource, new BusinessException(key));}}

ExceptionHandlerUtil工具类,可以将国际化配置中的内容通过MessageSource获取后解析后返回给前端,messageSource的key为自定义异常中的messageKey,参考后面的代码BusinessException。

/* 全局异常工具类. @author <a href="mailto:felix@gmail.com">lht</a>* @date 2023/4/17 16:27* @since 1.0.0/
@Slf4j
public class ExceptionHandlerUtil {private static final String RES_DEFAULT_ERROR_MSG = "global.server.internal.error";public ExceptionHandlerUtil() {}/* 全局异常. @param msgSource 国际化配置* @return responseEntity*/public static ResponseEntity<R> handleGlobalException(final MessageSource msgSource) {String msgValue = getGlobalMessage(RES_DEFAULT_ERROR_MSG);try {msgValue = msgSource.getMessage(RES_DEFAULT_ERROR_MSG, (Object[]) null, (Locale) null);} catch (NoSuchMessageException var4) {log.warn("解析messageKey:{} 全局异常错误信息:{}", RES_DEFAULT_ERROR_MSG, var4.getMessage());}return responseEntity(msgValue);}/* businessException异常处理. @param msgSource         国际化配置* @param businessException 异常* @return responseEntity*/public static ResponseEntity<R> handleBusinessException(final MessageSource msgSource, final BusinessException businessException) {String msgValue = getDefaultMessage(businessException.getMessageKey());try {msgValue = msgSource.getMessage(businessException.getMessageKey(), businessException.getValues(), (Locale) null);} catch (NoSuchMessageException var) {log.warn("BusinessException-解析messageKey:{} BusinessException-异常错误信息:{}", businessException.getMessageKey(), var.getMessage());}return responseEntity(msgValue);}/* responseEntity. @param msgValue 配置的值* @return responseEntity*/private static ResponseEntity<R> responseEntity(final String msgValue) {String[] values = msgValue.split(";");if (values.length < 3) {values = new String[]{String.valueOf(400), "system.unknown", "400"};}ResponseEntity<R> resultResponseEntity = new ResponseEntity(R.error(0, values[2] == null ? "400" : values[2], values[1] == null ? RES_DEFAULT_ERROR_MSG : values[1]), HttpStatus.//默认取400valueOf(Integer.parseInt(values[0])));return resultResponseEntity;}/* businessException默认的message. @param messageKey messageKey* @return messageKey*/private static String getDefaultMessage(final String messageKey) {return "400;" + messageKey + ";" + "400";}/* 全局异常message. @param messageKey* @return messageKey*/private static String getGlobalMessage(final String messageKey) {return "500;" + messageKey + ";" + "500";}
}

也可以自定义异常去匹配处理,如上面的@ExceptionHandler({BusinessException.class})的方法,BusinessException就是自定义异常处理类:


/* @author <a href="mailto:felix@gmail.com">lht</a>* @date 2023/4/17 14:39* @since 1.0.0/
public class BusinessException extends CommonException {public BusinessException(final String messageKey, final Object... values) {super(messageKey, values);}public BusinessException(final String messageKey) {super(messageKey);}public BusinessException(final Throwable cause, final String messageKey, final Object... values) {super(cause, messageKey, values);}public BusinessException(final Throwable cause, final String messageKey) {super(cause, messageKey);}}

当项目抛出BusinessException,会匹配到@ExceptionHandler({BusinessException.class})注解的方法,BusinessException的messageKey可以自定义,并且可以配置到国际化配置中。

示例

定义一个接口,抛出异常BusinessException。

优雅的处理sping项目全局异常

this.test.error是自定义配置在配置文件中的key,配置中的ASCII码含义是这是一个错误码测试,value后面的400001为返回的code码。

优雅的处理sping项目全局异常

请求后返回

优雅的处理sping项目全局异常