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@RestControllerAdvicepublic 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测试下
3、进一步理解
我们再通过一些问题来帮助你更深入理解@ControllerAdvice。
3.1、@ControllerAdvice还可以怎么用?
除了通过@ExceptionHandler注解用于全局异常的处理之外,@ControllerAdvice 还有两个用法:
- @InitBinder 注解
用于请求中注册自定义参数的解析,从而达到自定义请求参数格式的目的;
比如,在 @ControllerAdvice 注解的类中添加如下方法,来统一处理日期格式的格式化
@InitBinderpublic 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}.*/@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}/*** Initialize the strategy objects that this servlet uses.* 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); // requestMappinginitHandlerAdapters(context); // adaptersinitHandlerExceptionResolvers(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,作为设计者,就很容易出如下的结构
在RequestMappingHandlerAdapter中的afterPropertiesSet去处理advice
@Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();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.* 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
同样的在afterPropertiesSet去处理advice
@Overridepublic void afterPropertiesSet() {// Do this first, it may add ResponseBodyAdvice beansinitExceptionHandlerAdviceCache();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"})@Slf4jpublic 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("系统异常");}