全局异常处理
为了达到系统的各个模块中都能够共用同一个异常处理逻辑,避免代码重复和错误。在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 rel="nofollow" href="mailto:felix@gmail.com">lht</a>
* @date 2023/4/17 16:27
* @since 1.0.0
**/
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {
@Autowired
private 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 rel="nofollow" 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.
//默认取400
valueOf(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 rel="nofollow" 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。
this.test.error
是自定义配置在配置文件中的key,配置中的ASCII码含义是这是一个错误码测试
,value后面的400001为返回的code码。
请求后返回