SpringBoot第14讲:SpringBoot 如何统一异常处理

news2024/11/20 14:39:46

SpringBoot第14讲:SpringBoot 如何统一异常处理

本文是SpringBoot第14讲,SpringBoot接口如何对异常进行统一封装,并统一返回呢?以上文的参数校验为例,如何优雅的将参数校验的错误信息统一处理并封装返回呢

文章目录

  • SpringBoot第14讲:SpringBoot 如何统一异常处理
    • 1、为什么要优雅的处理异常
    • 2、实现案例
      • 2.1、@ControllerAdvice异常统一处理
      • 2.2、Controller接口
      • 2.3、运行测试
    • 3、进一步理解
      • 3.1、@ControllerAdvice还可以怎么用?
      • 3.2、@ControllerAdvice是如何起作用的(原理)?
    • 4、示例源码
    • 5、在商品中心的使用

1、为什么要优雅的处理异常

如果我们不统一的处理异常,经常会在controller层有大量的异常处理的代码, 比如:

@Slf4j
@Api(value = "User Interfaces", tags = "User Interfaces")
@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * http://localhost:8080/user/add .
     *
     * @param userParam user param
     * @return user
     */
    @ApiOperation("Add User")
    @ApiImplicitParam(name = "userParam", type = "body", dataTypeClass = UserParam.class, required = true)
    @PostMapping("add")
    public ResponseEntity<String> add(@Valid @RequestBody UserParam userParam) {
        // 每个接口充斥着大量的异常处理
        try {
            // do something
        } catch(Exception e) {
            return ResponseEntity.fail("error");
        }
        return ResponseEntity.ok("success");
    }
}

那怎么实现统一的异常处理,特别是结合参数校验等封装?

2、实现案例

简单展示通过 @ControllerAdvice 进行统一异常处理。

2.1、@ControllerAdvice异常统一处理

对于400参数错误异常

/**
 * Global exception handler.
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * exception handler for bad request.
     *
     * @param 	e exception
     * @return 	ResponseResult
     */
    @ResponseBody
    @ResponseStatus(code = HttpStatus.BAD_REQUEST)
    @ExceptionHandler(value = { BindException.class, ValidationException.class, MethodArgumentNotValidException.class })
    public ResponseResult<ExceptionData> handleParameterVerificationException(@NonNull Exception e) {
        ExceptionData.ExceptionDataBuilder exceptionDataBuilder = ExceptionData.builder();
        log.warn("Exception: {}", e.getMessage());
        if (e instanceof BindException) {
            BindingResult bindingResult = ((MethodArgumentNotValidException) e).getBindingResult();
            bindingResult.getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage)
                    .forEach(exceptionDataBuilder::error);
        } else if (e instanceof ConstraintViolationException) {
            if (e.getMessage() != null) {
                exceptionDataBuilder.error(e.getMessage());
            }
        } else {
            exceptionDataBuilder.error("invalid parameter");
        }
        return ResponseResultEntity.fail(exceptionDataBuilder.build(), "invalid parameter");
    }
}

对于自定义异常

/**
 * handle business exception.
 *
 * @param businessException
 *            business exception
 * @return ResponseResult
 */
@ResponseBody
@ExceptionHandler(BusinessException.class)
public ResponseResult<BusinessException> processBusinessException(BusinessException businessException) {
    log.error(businessException.getLocalizedMessage(), businessException);
    // 这里可以屏蔽掉后台的异常栈信息,直接返回"business error"
    return ResponseResultEntity.fail(businessException, businessException.getLocalizedMessage());
}

对于其它异常

/**
 * handle other exception.
 *
 * @param exception
 * @return ResponseResult
 */
@ResponseBody
@ExceptionHandler(Exception.class)
public ResponseResult<Exception> processException(Exception exception) {
    log.error(exception.getLocalizedMessage(), exception);
    // 这里可以屏蔽掉后台的异常栈信息,直接返回"server error"
    return ResponseResultEntity.fail(exception, exception.getLocalizedMessage());
}

2.2、Controller接口

(接口中无需处理异常)

@Slf4j
@Api(value = "User Interfaces", tags = "User Interfaces")
@RestController
@RequestMapping("/user")
public class UserController {

    /**
     * http://localhost:8080/user/add
     * @param userParam user param
     * @return user
     */
    @ApiOperation("Add User")
    @ApiImplicitParam(name = "userParam", type = "body", dataTypeClass = UserParam.class, required = true)
    @PostMapping("add")
    public ResponseEntity<UserParam> add(@Valid @RequestBody UserParam userParam) {
        return ResponseEntity.ok(userParam);
    }
}

2.3、运行测试

这里用postman测试下

  • img

3、进一步理解

我们再通过一些问题来帮助你更深入理解@ControllerAdvice。

3.1、@ControllerAdvice还可以怎么用?

除了通过@ExceptionHandler注解用于全局异常的处理之外,@ControllerAdvice 还有两个用法:

  • @InitBinder 注解

用于请求中注册自定义参数的解析,从而达到自定义请求参数格式的目的;

比如,在 @ControllerAdvice 注解的类中添加如下方法,来统一处理日期格式的格式化

@InitBinder
public void handleInitBinder(WebDataBinder dataBinder){
    dataBinder.registerCustomEditor(Date.class,
            new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));
}

Controller中传入参数(string类型)自动转化为Date类型

@GetMapping("testDate")
public Date processApi(Date date) {
    return date;
}
  • @ModelAttribute注解

用来预设全局参数,比如最典型的使用Spring Security时将添加当前登录的用户信息(UserDetails)作为参数。

@ModelAttribute("currentUser")
public UserDetails modelAttribute() {
    return (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

所有controller类中 requestMapping 方法都可以直接获取并使用 currentUser

@PostMapping("saveSomething")
public ResponseEntity<String> saveSomeObj(@ModelAttribute("currentUser") UserDetails operator) {
    // 保存操作,并设置当前操作人员的ID(从UserDetails中获得)
    return ResponseEntity.success("ok");
}

3.2、@ControllerAdvice是如何起作用的(原理)?

我们在 Spring第三讲:SpringMVC 从入门到精通 的基础上来看@ControllerAdvice的源码实现。

DispatcherServlet 中onRefresh方法是初始化ApplicationContext后的回调方法,它会调用initStrategies方法,主要更新一些servlet需要使用的对象,包括国际化处理,requestMapping,视图解析等等。

/**
	* This implementation calls {@link #initStrategies}.
  */
@Override
protected void onRefresh(ApplicationContext context) {
    initStrategies(context);
}

/**
	* Initialize the strategy objects that this servlet uses.
  * <p>May be overridden in subclasses in order to initialize further strategy objects.
  */
protected void initStrategies(ApplicationContext context) {
    initMultipartResolver(context); // 文件上传
    initLocaleResolver(context); // i18n国际化
    initThemeResolver(context); // 主题
    initHandlerMappings(context); // requestMapping
    initHandlerAdapters(context); // adapters
    initHandlerExceptionResolvers(context); // 异常处理
    initRequestToViewNameTranslator(context);
    initViewResolvers(context);
    initFlashMapManager(context);
}

从上述代码看,如果要提供@ControllerAdvice提供的三种注解功能,从设计和实现的角度肯定是实现的代码需要放在initStrategies方法中。

  • @ModelAttribute和@InitBinder处理

具体来看,如果你是设计者,很显然容易想到:对于@ModelAttribute提供的参数预置和@InitBinder注解提供的预处理方法应该是放在一个方法中的,因为它们都是在进入 requestMapping 方法前做的操作。

如下方法是获取所有的HandlerAdapter,无非就是从BeanFactory中获取(BeanFactory相关知识请参考 Spring进阶- Spring IOC实现原理详解之IOC体系结构设计)

private void initHandlerAdapters(ApplicationContext context) {
    this.handlerAdapters = null;

    if (this.detectAllHandlerAdapters) {
        // Find all HandlerAdapters in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerAdapter> matchingBeans =
                BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerAdapters = new ArrayList<>(matchingBeans.values());
            // We keep HandlerAdapters in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerAdapters);
        }
    }else {
        try {
            HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
            this.handlerAdapters = Collections.singletonList(ha);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, we'll add a default HandlerAdapter later.
        }
    }

    // Ensure we have at least some HandlerAdapters, by registering
    // default HandlerAdapters if no other adapters are found.
    if (this.handlerAdapters == null) {
        this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

我们要处理的是requestMapping的handlerResolver,作为设计者,就很容易出如下的结构

  • img

在RequestMappingHandlerAdapter中的afterPropertiesSet去处理advice

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBody advice beans
    initControllerAdviceCache();

    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }

    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();

    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        // 缓存所有modelAttribute注解方法
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {
            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }
        // 缓存所有initBinder注解方法
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {
            this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }

    if (!requestResponseBodyAdviceBeans.isEmpty()) {
        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }
}
  • @ExceptionHandler处理

@ExceptionHandler显然是在上述 initHandlerExceptionResolvers(context) 方法中。

同样的,从 BeanFactory 中获取 HandlerExceptionResolver

/**
    * Initialize the HandlerExceptionResolver used by this class.
    * <p>If no bean is defined with the given name in the BeanFactory for this namespace,
    * we default to no exception resolver.
    */
private void initHandlerExceptionResolvers(ApplicationContext context) {
    this.handlerExceptionResolvers = null;

    if (this.detectAllHandlerExceptionResolvers) {
        // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
        Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
                .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
        if (!matchingBeans.isEmpty()) {
            this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
            // We keep HandlerExceptionResolvers in sorted order.
            AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
        }
    }
    else {
        try {
            HandlerExceptionResolver her =
                    context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
            this.handlerExceptionResolvers = Collections.singletonList(her);
        }
        catch (NoSuchBeanDefinitionException ex) {
            // Ignore, no HandlerExceptionResolver is fine too.
        }
    }

    // Ensure we have at least some HandlerExceptionResolvers, by registering
    // default HandlerExceptionResolvers if no other resolvers are found.
    if (this.handlerExceptionResolvers == null) {
        this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
        if (logger.isTraceEnabled()) {
            logger.trace("No HandlerExceptionResolvers declared in servlet '" + getServletName() +
                    "': using default strategies from DispatcherServlet.properties");
        }
    }
}

我们很容易找到ExceptionHandlerExceptionResolver
img

同样的在afterPropertiesSet去处理advice

@Override
public void afterPropertiesSet() {
    // Do this first, it may add ResponseBodyAdvice beans
    initExceptionHandlerAdviceCache();

    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

private void initExceptionHandlerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }

    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
        if (resolver.hasExceptionMappings()) {
            this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
        }
        if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            this.responseBodyAdvice.add(adviceBean);
        }
    }
}

4、示例源码

todo

5、在商品中心的使用

web层通用处理逻辑 ControllerErrorHandler

@RestControllerAdvice(basePackages = {
        "cn.xxx.item.web.controller",
        "cn.xxx.service.warehouse.web.controller",
        "cn.xxx.service.category.web.controller",
        "cn.xxx.service.address.web.controller",
        "cn.xxx.service.brand.web.controller",
        "cn.xxx.service.file.web.controller",
        "cn.xxx.service.agreement.item.controller",
        "cn.xxx.service.audit.controller"
})
@Slf4j
public class ControllerErrorHandler {

    // [异常处理][errorHandler][转义信息][URL:][其他信息][异常信息]
    private static final String LOG_FORMAT = "[商品聚合web服务][ControllerErrorHandler][{}][URL:{}][{}][{}]";//

    private static final String PARAM_ERROR_MESSAGE = "输入参数不合法";
    private static final String DEFAULT_ERROR_CODE = "400";

    /**
     * Valid标签 校验失败
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, IllegalArgumentException ex) {
        String errMsg = ex.getMessage();
        log.warn(LOG_FORMAT, errMsg, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return Response.fail(errMsg);
    }

    /**
     * Valid标签 校验失败
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, BindException ex) {
        String errMsg;
        List<FieldError> fieldErrors = ex.getFieldErrors();
        if (CollectionUtils.isEmpty(fieldErrors)) {
            errMsg = PARAM_ERROR_MESSAGE;
        } else {
            errMsg = fieldErrors.get(0).getDefaultMessage();
        }
        log.warn(LOG_FORMAT, errMsg, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return Response.fail(PARAM_ERROR_MESSAGE);
    }

    /**
     * Validated标签 校验失败
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, ConstraintViolationException ex) {
        String errMsg;
        Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
        if (CollectionUtils.isEmpty(constraintViolations)) {
            errMsg = PARAM_ERROR_MESSAGE;
        } else {
            ConstraintViolation<?> next = constraintViolations.iterator().next();
            errMsg = next.getMessage();
        }
        log.warn(LOG_FORMAT, errMsg, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return Response.fail(errMsg);
    }

    /**
     * required=true 校验失败
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request,
                                           MissingServletRequestParameterException ex) {
        log.warn(LOG_FORMAT, "缺少必要参数", this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return Response.fail("缺少必要参数");
    }

    /**
     * JSR303注解参数 校验失败
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processJsr303ValidatorError(NativeWebRequest request, MethodArgumentNotValidException ex) {
        BindingResult errors = ex.getBindingResult();
        String errMsg;
        if (errors == null || CollectionUtils.isEmpty(errors.getFieldErrors())) {
            errMsg = PARAM_ERROR_MESSAGE;
        } else {
            errMsg = errors.getFieldErrors().get(0).getDefaultMessage();
        }
        log.warn(LOG_FORMAT, errMsg, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return Response.fail(errMsg);
    }

    /**
     * 输入参数数据类型转换错误
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, HttpMessageNotReadableException ex) {
        log.warn(LOG_FORMAT, PARAM_ERROR_MESSAGE, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return Response.fail(PARAM_ERROR_MESSAGE);
    }

    /**
     * 输入参数数据类型转换错误
     */
    @ExceptionHandler(TypeMismatchException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, TypeMismatchException ex) {
        log.warn(LOG_FORMAT, PARAM_ERROR_MESSAGE, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return Response.fail(PARAM_ERROR_MESSAGE);
    }

    /**
     * 连接超时
     */
    @ExceptionHandler(TimeoutException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, TimeoutException ex) {
        log.warn(LOG_FORMAT, CommonError.TIMEOUT_ERROR, this.getRequestMapping(request), SerializeUtil.getObjString
                (request.getParameterMap()), ex);
        return Response.fail(CommonError.TIMEOUT_ERROR.getErrorMessage());
    }

    /**
     * dubbo异常
     */
    @ExceptionHandler(RpcException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, RpcException ex) {
        log.error(LOG_FORMAT, CommonError.DUBBO_RPC_ERROR, this.getRequestMapping(request), SerializeUtil
                .getObjString(request.getParameterMap()), ex);
        return Response.fail(CommonError.DUBBO_RPC_ERROR.getErrorMessage());
    }

    /**
     * 业务异常, warn日志
     */
    @ExceptionHandler(ServiceResponseException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, ServiceResponseException ex) {
        String message = ex.getMessage();
        log.warn(LOG_FORMAT, message, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return Response.fail(message);
    }

    /**
     * 业务异常, warn日志
     */
    @ExceptionHandler(ServiceException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String processControllerError(NativeWebRequest request, ServiceException ex) {
        String message = ex.getMessage();
        log.warn(LOG_FORMAT, message, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return message;
    }

    /**
     * 业务异常, warn日志
     */
    @ExceptionHandler(JsonResponseException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public String processJsonResponseError(NativeWebRequest request, JsonResponseException ex) {
        String message = ex.getMessage();
        log.warn(LOG_FORMAT, message, this.getRequestMapping(request), SerializeUtil.getObjString(request
                .getParameterMap()), ex);
        return message;
    }


    /**
     * spring封装的数据库异常
     */
    @ExceptionHandler(DataAccessException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, DataAccessException ex) {
        log.error(LOG_FORMAT, CommonError.DB_ERROR.getErrorMessage(), this.getRequestMapping(request), SerializeUtil
                .getObjString(request.getParameterMap()), ex);
        return Response.fail(CommonError.DB_ERROR.getErrorMessage());
    }

    /**
     * spring未捕获的数据库异常
     */
    @ExceptionHandler(SQLException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, SQLException ex) {
        log.error(LOG_FORMAT, CommonError.DB_ERROR.getErrorMessage(), this.getRequestMapping(request), SerializeUtil
                .getObjString(request.getParameterMap()), ex);
        return Response.fail(CommonError.DB_ERROR.getErrorMessage());
    }

    /**
     * 兜底1
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, RuntimeException ex) {
        log.error(LOG_FORMAT, CommonError.SYSTEM_ERROR.getErrorMessage(), this.getRequestMapping(request),
                SerializeUtil.getObjString(request.getParameterMap()), ex);
        return Response.fail(CommonError.SYSTEM_ERROR.getErrorMessage());
    }

    /**
     * 兜底2
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, Exception ex) {
        log.error(LOG_FORMAT, CommonError.SYSTEM_ERROR.getErrorMessage(), this.getRequestMapping(request),
                SerializeUtil.getObjString(request.getParameterMap()), ex);
        return Response.fail(CommonError.SYSTEM_ERROR.getErrorMessage());
    }

    /**
     * 兜底3
     */
    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.OK)
    public Response processControllerError(NativeWebRequest request, Throwable ex) {
        log.error(LOG_FORMAT, CommonError.SYSTEM_ERROR.getErrorMessage(), this.getRequestMapping(request),
                SerializeUtil.getObjString(request.getParameterMap()), ex);
        return Response.fail(CommonError.SYSTEM_ERROR.getErrorMessage());
    }

    private String getRequestMapping(NativeWebRequest nativeWebRequest) {
        try {
            HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
            if (request != null) {
                return request.getRequestURI();
            }
        } catch (Exception ignore) {
        }
        return "--";
    }
}

IP通用异常拦截

@Slf4j
@Component
@Aspect
@Order(0)
public class ExceptionAspect {

    @Value("${logApiParam:false}")
    boolean logApiParam;

    @Pointcut("execution(public cn.xxx.common.api.Response cn.xxx.itemplatform.web..*Controller.*(..))")
    public void publicRespWebMethod() {
    }

    @Pointcut("execution(public cn.xxx.common.api.Response cn.xxx.itemplatform.app..api..*Impl.*(..))")
    public void publicRespApiMethod() {
    }

    @Pointcut("execution(public cn.xxx.common.api.Response cn.xxx.itemplatform.app.open.cloudservice..*Impl.*(..))")
    public void publicRespCloudServiceMethod() {
    }

    @Pointcut("execution(public cn.xxx.common.api.Response cn.xxx.itemplatform.open.api..*Api.*(..))")
    public void publicRespCloudApiMethod() {
    }

    @Before(value = "publicRespApiMethod() || publicRespCloudServiceMethod() || publicRespWebMethod() || publicRespCloudApiMethod()")
    public void logBefore(final JoinPoint point) {
        logApiParam(point);
    }

    private void logApiParam(JoinPoint point) {
        if (!logApiParam) {
            return;
        }
        try {
            String targetClassName = point.getTarget().getClass().getName();
            String methodName = point.getSignature().getName();
            log.info("【logApiParam】class:{} , method: {} , params:{}", targetClassName, methodName, ItemPlatformJsonUtil.objToJson(point.getArgs()));
        } catch (Throwable t) {
        }
    }

    @Around("publicRespApiMethod() || publicRespCloudServiceMethod() || publicRespWebMethod() || publicRespCloudApiMethod()")
    public Object around(ProceedingJoinPoint joinPoint) {
        ResponseSupplierUtil.CallSourceEnum callSourceEnum = null;
        //推测调用来源
        try {
            String targetClassName = joinPoint.getTarget().getClass().getName();
            if (targetClassName.startsWith("cn.xxx.itemplatform.app.open") ||
                    targetClassName.startsWith("cn.xxx.itemplatform.open.api")) {
                callSourceEnum = ResponseSupplierUtil.CallSourceEnum.OPEN_PLATFORM;
            }
        } catch (Throwable t) {
        }
        return ResponseSupplierUtil.getResponseWithExpHandler(() -> (Response) joinPoint.proceed(), callSourceEnum);
    }
}

 public static <T> Response<T> getResponseWithExpHandler(ResponseSupplier<Response<T>> responseSupplier
            , @Nullable CallSourceEnum callSourceEnum) {
        Response<T> response = getNormalResponse(responseSupplier);
        if (CallSourceEnum.OPEN_PLATFORM.equals(callSourceEnum)) {
            return toOpenPlatformResponse(response);
        }
        return response;
}

private static <T> Response<T> getNormalResponse(ResponseSupplier<Response<T>> responseSupplier) {
        try {
            return responseSupplier.get();
        } catch (ServiceException serviceException) {
            return Response.failOfMessage(serviceException.getMessage());
        } catch (BaseException baseException) {
            return handle(baseException);
        } catch (ConstraintViolationException constraintViolationException) {
            List<String> message = constraintViolationException.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.toList());
            return Response.fail(ErrorCodeEnum.BIZ_ERROR_PARAM.getErrCode(), String.join(",", message));
        } catch (Throwable throwable) {
            log.error("", throwable);
            return Response.failOfMessage("系统异常");
        }
}

private static <T> Response<T> handle(BaseException baseException) {
        ErrorCode errCode = baseException.getErrCode();
        ErrorCodeEnum errorCodeEnum = ErrorCodeEnum.from(errCode.getErrCode());
        Level level = Level.ERROR;
        if (errCode == null) {
            log.error("无效的errCode:{},errorCode必须在 cn.gov.zcy.itemplatform.common.exception.ErrorCodeEnum 中定义", errCode.getErrCode());
        } else {
            level = errorCodeEnum.getLogLevel();
        }

        //按级别打日志
        switch (level) {
            case ERROR:
                log.error("{}", errCode, baseException);
                break;
            case WARN:
                log.warn("{}", errCode, baseException);
                break;
            case INFO:
                log.info("{}", errCode, baseException);
                break;
            case DEBUG:
                log.debug("{}", errCode, baseException);
                break;
            case TRACE:
                log.trace("{}", errCode, baseException);
                break;
            default:
                log.error("{}", errCode, baseException);
                break;

        }
        return baseException.toResponse("系统异常");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/680614.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

诊断测试工具CANoe.DiVa从入门到精通系列——开门见山

我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 人们会在生活中不断攻击你。他们的主要武器是向你灌输对自己的怀疑:你的价值、你的能力、你的潜力。他们往往会将此伪装成客观意见,但无一例外的是,他们想…

网络安全就业前景如何?是否还能入行?

网络安全专业是2015年新设立的专业&#xff0c;作为新兴专业吸引了很多人准备入行&#xff0c;那么它的就业前景怎么样&#xff1f;大致可以分为3个版块来介绍。 1.就业领域前景广阔 目前互联网、通信、新能源、房地产、金融证券、电子技术等行业迫切需要网络安全人才&#x…

22. 算法之图的最短路径

前言 关于图的最短路径问题&#xff0c;是图这种数据结构中的经典问题。也是与我们的生活息息相关的&#xff0c;比如上海四通八达的地铁线路&#xff0c;从一个地铁站&#xff0c;到另一个地铁站&#xff0c;可能有很多种不同的路线。那么&#xff0c;我们选哪种路线&#xf…

JavaFX第五篇 Image图片加载处理

JavaFX第五篇 Image图片加载处理 1. 代码2. 讲解3. 代码仓 图片已经成为每个网站的必备了&#xff0c;不仅可以提升个人网站的标识度而且还可以美化网站&#xff0c; 所以这里需要讲解一下如何加载图片&#xff0c;展示到前台给用户查看。 本次只是简单的讲解如何展示使用&…

【算法证明 七】深入理解深度优先搜索

深度优先搜索包含一个递归&#xff0c;对其进行分析要复杂一些。与上一篇文章一样&#xff0c;还是给节点定义几个状态&#xff0c;然后详细分析深度优先搜索算法有哪些性质。 算法描述 定义状态 v . c o l o r &#xff1a;初始状态为白色&#xff0c;被发现时改为灰色&…

Mysql的SQL性能分析【借助EXPLAIN分析】

性能分析 要说sql有问题&#xff0c;需要拿出证据&#xff0c;因此需要性能分析 Mysql查询优化器&#xff08;Mysql Query Optimizer&#xff09; Mysql中有专门负责优化SELECT语句的优化器模块&#xff0c;主要功能&#xff1a;通过计算分析系统中收集到的统计信息&#xf…

Xline v0.4.1: 一个用于元数据管理的分布式KV存储

Xline是什么&#xff1f;我们为什么要做Xline&#xff1f; Xline是一个基于Curp协议的&#xff0c;用于管理元数据的分布式KV存储。现有的分布式KV存储大多采用Raft共识协议&#xff0c;需要两次RTT才能完成一次请求。当部署在单个数据中心时&#xff0c;节点之间的延迟较低&a…

python机器学习——分类模型评估 分类算法(k近邻,朴素贝叶斯,决策树,随机森林,逻辑回归,svm)

目录 分类模型的评估模型优化与选择1.交叉验证2.网格搜索 【分类】K近邻算法【分类】朴素贝叶斯——文本分类实例&#xff1a;新闻数据分类 【分类】决策树和随机森林1.决策树2.决策树的算法3.代码实现实例&#xff1a;泰坦尼克号预测生死 【集成学习】随机森林1.集成学习2.随机…

LOMO:在受限资源上全参数微调

LOMO&#xff1a;Full Parameter Fine-Tuning for large language models with limited resources IntroductionMethodRethink the functionality of optimizerUsing SGD LOMO&#xff1a; LOw-Memory Optimization 实验参考 Introduction 在这篇文章中&#xff0c;作者的目的…

Go 语言进阶 - 工程进阶

前言&#xff1a; \textcolor{Green}{前言&#xff1a;} 前言&#xff1a; &#x1f49e;这个专栏就专门来记录一下寒假参加的第五期字节跳动训练营 &#x1f49e;从这个专栏里面可以迅速获得Go的知识 今天的内容包括以下两个内容。关于实践的内容我会在后续发布出来。 01.语言…

新零售破局丨2023年探索全新电商运维模式——永倍达模式深度解析

新零售破局丨2023年探索全新电商运维模式——永倍达模式深度解析 大家好&#xff01;我是微三云胡佳东&#xff0c;一家专业的电商软件开发公司的负责人。 近年来&#xff0c;随着电商的高速发展&#xff0c;不少电商平台成为了市场经济的优质榜样&#xff0c;互联网市场竞争也…

设计模型学习-UML图

1&#xff0c;简介 UML图有很多种类型&#xff0c;但掌握其中的类图、用例图和时序图就可以完成大部分的工作。其中最重要的便是「类图」&#xff0c;它是面向对象建模中最常用和最重要的图&#xff0c;是定义其他图的基础。 类图主要是用来显示系统中的类、接口以及它们之间的…

Ubuntu环境下读取罗技G29方向盘信息

本篇博客最早发布于实验室公共博客&#xff0c;但已无人维护&#xff0c;现迁移至个人博客 引言 实验室有这么酷的驾驶设备&#xff0c;来了一年还没有实际操作过&#xff0c;早就蠢蠢欲试了&#xff0c;哈哈哈不过之前负责的师兄还在就一直没敢用&#xff0c;现在他毕业了就可…

rust abc(2): 从 hello world 到整数、浮点类型

文章目录 1. 目的2. 搞懂 hello world2.1 代码2.2 fn 的含义2.3 main() 的含义2.4 println! 的含义2.5 行尾分号是必要的吗&#xff1f;2.6 左花括号可以放下一行吗&#xff1f; 3. 数据类型的例子3.1 代码3.2 rust 的注释3.3 编译运行结果3.4 基本数据类型 4. 整数类型的例子4…

SpringMVC系列-3 拦截器

背景 本文作为 SpringMVC系列 的第三篇&#xff0c;以SpringMVC系列-2 HTTP请求调用链为基础&#xff0c;介绍Spring MVC的拦截器。 1.拦截器 SpringMVC的核心实现是DispatcherServlet&#xff0c;本质是一个Servlet实现类&#xff0c;拦截器位于DispatcherServlet逻辑中&am…

MySQL进阶SQL语句2之表连接

目录 1.连接查询 1.1inner&#xff08;内连接&#xff09; 1.2left join&#xff08;左连接&#xff09; 1.3right join&#xff08;右连接&#xff09; 1.4直接查询两个表相同的字段值的数据 2. VIEW&#xff08;视图&#xff09; 2.1create view&#xff08;创建视图…

设计模式之迭代器模式笔记

设计模式之迭代器模式笔记 说明Iterator(迭代器)目录迭代器模式示例类图学生类抽象迭代器角色接口具体迭代器角色类抽象聚合角色接口具体聚合角色类测试类 说明 记录下学习设计模式-迭代器模式的写法。JDK使用版本为1.8版本。 Iterator(迭代器) 意图:提供一种方法顺序访问一…

Python2、3下载安装、环境配置和Python2、3版本共存配置

一、python 版本简介 python 包括 python2、python3 两个大版本&#xff0c;其中 python3 改进了 python2 的一些不足&#xff0c;但由于以前很多应用是用 python2 开发的&#xff0c;维护这些应用还需用到 python2&#xff0c;故 python2 尚未被完全淘汰。 北京时间 2020 年 4…

近期参与开源的心得体会

引言 最近随着Kepler项目加入CNCF sandbox&#xff0c;写一篇blog来记录下参与这个项目半年的发展的心得体会。 运营 项目的运营最好还是专注于项目自身的发展&#xff0c;围绕项目的特点&#xff0c;创新点入手&#xff0c;为大家提供价值&#xff0c;从而自然而然的扩大自…

【计算机网络】计算机网络期末自测题(一)答案

2019-2020 学年第 2 学期自测题答案及评分标准 (卷 1) 计算机网络 一、 填空题&#xff1a; 参考答案&#xff1a; 1 、 01000101 、11100111 3 、 100Mbps、双绞线、基带、全双工 [10Mbps 要求单位] 4 、 报文 5 、 ICMP 6 、 虚电路 7 、 距离矢量、链路状态 …