一 背景
日常开发中,我们通常基于@ControllerAdivce和springboot的ErrorController对代码中抛出的异常进行统一的拦截,返回统一的格式,但是随着业务的持续迭代升级,对不同场景下的异常业务有不同的处理,比如有点异常需要国际化,有的异常需要消息通知,如果都同一拦截处理不同的异常会导致改拦截体中的方法越来越多.
本文针对上述场景基于策略模式,构造者模式等实现对异常处理统一封装,并作为springboot的自定义starter
二 整体实现思路
- 将异常的处理统一抽象成ExceptionTranslator接口,根据不同的业务实现适配不同的业务异常
- 将不同的业务实现注册到ExceptionTranslatorRegister中
- handler中依赖ExceptionTranslatorExecutor
- ExceptionTranslatorExecutor中调用注册到ExceptionTranslatorRegister中的ExceptionTranslator对遍历适配到相应的翻译器对当前异常进行翻译
三 代码实现
定义异常翻译接口
public interface Support {
/**
* 是否支持当前异常
* @param throwable
* @return
*/
Boolean support(Throwable throwable);
}
public interface ExceptionTranslator extends Support {
/**
* 异常翻译
* @param throwable
* @return
*/
ApiResult translate(Throwable throwable);
}
抽象实现
@Slf4j
public abstract class AbstractExceptionTranslator implements ExceptionTranslator, InitializingBean {
@Override
public ApiResult translate(Throwable throwable) {
if (!support(throwable)){
log.info("{} not support for {}", this.getClass().getName(), throwable.toString());
return null;
}
log.info("{} will be handled by {}.", throwable.toString(), this.getClass().getSimpleName());
return doTranslate(throwable);
}
/**
* 注入实例化bean中的翻译器
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
ExceptionTranslatorRegister.register(this);
}
/**
* 实例的实现
* @param throwable
* @return
*/
protected abstract ApiResult doTranslate(Throwable throwable);
}
不同异常实现
public class CommonBusinessExceptionTranslator extends AbstractExceptionTranslator {
@Override
protected ApiResult doTranslate(Throwable throwable) {
return ApiResultBuildr.build(GlobalErrorConstants.INTERNAL_SERVER_ERROR.getCode(), throwable.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
@Override
public Boolean support(Throwable throwable) {
return throwable instanceof BaseBusinessException;
}
}
public class UnsupportedExceptionTranslator extends AbstractExceptionTranslator {
@Override
protected ApiResult doTranslate(Throwable throwable) {
return ApiResultBuildr.build(GlobalErrorConstants.INTERNAL_SERVER_ERROR.getCode(), GlobalErrorConstants.INTERNAL_SERVER_ERROR.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
@Override
public Boolean support(Throwable throwable) {
return true;
}
}
统一注册
@Slf4j
public class ExceptionTranslatorRegister {
private static final Collection<ExceptionTranslator> EXCEPTION_TRANSLATORS = Collections.synchronizedSet(new LinkedHashSet<>());
/**
* 初始化默认的翻译器
*/
static {
/**
* 系统调用链反向顺序(顺序有关优先级)
*/
register(new UnsupportedExceptionTranslator());
register(new CommonBusinessExceptionTranslator());
register(new LocalBusinessExceptionTranslator());
//web mvc
register(new HttpMethodNotSupportedExceptionTranslator());
register(new MethodArgumentNotValidExceptionTranslator());
register(new WebBindingExceptionTranslator());
register(new ValidationExceptionTranslator());
register(new NoHandlerFoundExceptionTranslator());
register(new RequestNotMappingExceptionTranslator());
register(new ConstraintViolationExceptionTranslator());
register(new ServletRequestBindingExceptionTranslator());
}
public static boolean register(ExceptionTranslator exceptionTranslator) {
return EXCEPTION_TRANSLATORS.add(exceptionTranslator);
}
public static Collection<ExceptionTranslator> getAll() {
List<ExceptionTranslator> temp = new ArrayList<>(EXCEPTION_TRANSLATORS);
Collections.reverse(temp);
log.debug("current registered exception translators: {}", temp);
return Collections.unmodifiableCollection(temp);
}
}
定义执行器
@Slf4j
public class ExceptionTranslatorExecutor implements ExceptionTranslator {
@Override
public ApiResult translate(Throwable throwable) {
log.error("error: ", throwable);
for (ExceptionTranslator translator: ExceptionTranslatorRegister.getAll()) {
if (!translator.support(throwable)) {
continue;
}
ApiResult apiResult = translator.translate(throwable);
if (apiResult != null) {
return apiResult;
}
}
log.warn("none exception translator matched. default result returned.");
return ApiResultBuildr.build(GlobalErrorConstants.INTERNAL_SERVER_ERROR.getCode(), GlobalErrorConstants.INTERNAL_SERVER_ERROR.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
@Override
public Boolean support(Throwable throwable) {
return Boolean.TRUE;
}
}
拦截器中调用
@RequestMapping("${server.error.path:${error.path:/error}}")
public class GlobalExceptionController extends BasicErrorController {
private final ExceptionTranslator exceptionTranslator;
private final ErrorAttributes errorAttributes;
public GlobalExceptionController(ExceptionTranslator exceptionTranslator, ErrorAttributes errorAttributes, ErrorProperties errorProperties) {
super(errorAttributes, errorProperties);
this.errorAttributes = errorAttributes;
this.exceptionTranslator = exceptionTranslator;
}
@Override
@RequestMapping
@ResponseBody
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
LocalResult localResult;
HttpStatus httpStatus = getStatus(request);
if (httpStatus.equals(HttpStatus.NOT_FOUND)) {
localResult = new LocalResult();
localResult.setCode(httpStatus.value());
localResult.setMessage(GlobalErrorConstants.NOT_FOUND.getMessage());
} else {
ServletWebRequest servletWebRequest = new ServletWebRequest(request);
Throwable error = errorAttributes.getError(servletWebRequest);
if (error != null) {
error = ExceptionUtil.getRootCause(error);
ApiResult translate = exceptionTranslator.translate(error);
localResult = translate.getBody();
httpStatus = translate.getHttpStatus();
} else {
localResult = new LocalResult();
localResult.setCode(httpStatus.value());
localResult.setMessage(GlobalErrorConstants.INTERNAL_SERVER_ERROR.getMessage());
}
}
Map<String, Object> body = BeanUtil.beanToMap(localResult);
return new ResponseEntity<>(body, httpStatus);
}
}
@RestControllerAdvice
public class GlobalExceptionHandler {
private final ExceptionTranslator exceptionTranslator;
public GlobalExceptionHandler(ExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator;
}
@ExceptionHandler(Exception.class)
public ResponseEntity<LocalResult> handleException(Exception e) {
return exceptionTranslator.translate(e).toResponseEntity();
}
}
bean注入
public class ExceptionConfig {
/**
* 翻译执行器
* @return
*/
@ConditionalOnMissingBean
@Bean
public ExceptionTranslator exceptionTranslator() {
return new ExceptionTranslatorExecutor();
}
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@Bean
public GlobalExceptionController globalExceptionController(ExceptionTranslator exceptionTranslator, ErrorAttributes errorAttributes, ServerProperties serverProperties) {
return new GlobalExceptionController(exceptionTranslator, errorAttributes, serverProperties.getError());
}
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@Bean
public GlobalExceptionHandler globalExceptionHandler(ExceptionTranslator exceptionTranslator) {
return new GlobalExceptionHandler(exceptionTranslator);
}
}
@Configuration
@Import({ExceptionConfig.class})
@AutoConfigureBefore(ErrorMvcAutoConfiguration.class)
public class ExceptionHandlerAutoConfiguration {
}