扩展springboot内置异常拦截器--自定义全局异常处理组件

一 背景

日常开发中,我们通常基于@ControllerAdivce和springboot的ErrorController对代码中抛出的异常进行统一的拦截,返回统一的格式,但是随着业务的持续迭代升级,对不同场景下的异常业务有不同的处理,比如有点异常需要国际化,有的异常需要消息通知,如果都同一拦截处理不同的异常会导致改拦截体中的方法越来越多.
本文针对上述场景基于策略模式,构造者模式等实现对异常处理统一封装,并作为springboot的自定义starter

二 整体实现思路

在这里插入图片描述

  1. 将异常的处理统一抽象成ExceptionTranslator接口,根据不同的业务实现适配不同的业务异常
  2. 将不同的业务实现注册到ExceptionTranslatorRegister中
  3. handler中依赖ExceptionTranslatorExecutor
  4. 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 {

}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值