统一异常处理概述
spring mvc中如何做统一异常处理
先加载的@ControllerAdvice类里如果存在@ExceptionHandler(xxException.class)是需要捕获的异常或其父类,则将使用先加载的类中的异常处理方式。如果没有,则看后面的@ControllerAdvice类里是否有。
可以使用@Order来决定加载优先级,网上也有说法可以使用@Primary,暂未自测,个人觉得应该也是可行的。
如果有多个统一异常处理怎么办
- 使用
basePackageClasses属性,将想要优先加载的包写在前面; - 使用@Order注解来决定顺序
- 使用
excludeFilters属性排除不想要加载的类,该属性使用方式多样,可自行搜索查看。(例:@ComponentScan(excludeFilters = @ComponentScan.Filter( type = FilterType.ASSIGNABLE_TYPE, classes = xx.class)))
项目中的统一异常处理
package com.yonyoucloud.manufacturing.web;import com.yonyou.ucf.mdd.ext.util.ResultMessage;import org.imeta.biz.base.BizException;import org.springframework.http.HttpStatus;import org.springframework.http.MediaType;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import org.springframework.web.servlet.NoHandlerFoundException;import javax.servlet.http.HttpServletRequest;@ControllerAdvicepublic class ErrorHandler {@ExceptionHandler(BizException.class)@ResponseBodyResponseEntity<?> handleBizException(HttpServletRequest request, Throwable ex) {//HttpStatus status = getStatus(request);return errResponse(ResultMessage.error(ex.getMessage()), HttpStatus.OK);}@ExceptionHandler(NoHandlerFoundException.class)@ResponseBodyResponseEntity<?> handleNotFoundException(HttpServletRequest request, Throwable ex) {//HttpStatus status = getStatus(request);return errResponse(ResultMessage.error(String.format("接口无效:%s", request.getRequestURI())), HttpStatus.OK);}@ExceptionHandler(Throwable.class)@ResponseBodyResponseEntity<?> handleCommonException(HttpServletRequest request, Throwable ex) {HttpStatus status = getStatus(request);return errResponse(ResultMessage.error(ex.getMessage()), status);}private HttpStatus getStatus(HttpServletRequest request) {Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");if (statusCode == null) {return HttpStatus.INTERNAL_SERVER_ERROR;}return HttpStatus.valueOf(statusCode);}private ResponseEntity<?> errResponse(String message, HttpStatus status) {return ResponseEntity.status(status).contentType(MediaType.APPLICATION_JSON_UTF8).body(message);}}
平台的统一异常处理
mdd框架中也有类似的处理
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package com.yonyou.iuap.ucf.core.exception;import com.yonyou.iuap.ucf.common.exception.BusinessException;import com.yonyou.iuap.ucf.common.exception.CommonExceptionConstants;import com.yonyou.iuap.ucf.common.rest.CommonResponse;import org.apache.commons.lang3.exception.ExceptionUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.slf4j.MDC;import org.springframework.beans.factory.annotation.Value;import org.springframework.web.bind.annotation.ControllerAdvice;import org.springframework.web.bind.annotation.ExceptionHandler;import org.springframework.web.bind.annotation.ResponseBody;@ControllerAdvicepublic class CommonExceptionHandler {private static final Logger log = LoggerFactory.getLogger(CommonExceptionHandler.class);@Value("${ucf.enableExcepthonHandler:true}")private String enableExcepthonHandler;public CommonExceptionHandler() {}@ExceptionHandler({Throwable.class})public CommonResponse handleThrowable(final Throwable e) throws Throwable {if (this.enableExcepthonHandler != null && !"true".equals(this.enableExcepthonHandler)) {throw e;} else {return this.handleBusinessException(new BusinessException(CommonExceptionConstants.UNKNOWN_EXCEPTION, e));}}@ExceptionHandler({BusinessException.class})@ResponseBodypublic CommonResponse handleBusinessException(final BusinessException excetpion) {try {MDC.put("code", String.valueOf(excetpion.getErrorCode()));if (excetpion.getCause() != null) {log.error("出现异常啦", excetpion);} else {String[] stackFrames = ExceptionUtils.getStackFrames(excetpion);String latestUsableStack = "";if (stackFrames != null && stackFrames.length > 1) {latestUsableStack = stackFrames[1];}log.error("出现异常啦:" + excetpion.getMessage() + ",异常编码:" + excetpion.getErrorCode() + ",异常发生地:" + latestUsableStack);}} finally {MDC.remove("code");}return CommonResponse.ofFail(excetpion);}}
这样,咱们的项目运行起来就会有两个@ControllerAdvice的异常处理器,那到底会用哪个呢?
- 没有打@Order注解
因为没有打@Order注解,所以就取决于scan包的顺序了。在config项目的applicationContext中,看到了这样的配置
<context:component-scan base-package="com.yonyoucloud,com.yonyou"><context:exclude-filter type="assignable" expression="org.imeta.orm.base.BizObject"/><context:exclude-filter type="regex" expression="com.yonyou.ucf.mdd.ext.controller.FileUpload"/><context:exclude-filter type="regex"expression="com.yonyou.ucf.mdd.ext.portal.controller.PortalLayoutController"/><context:exclude-filter type="regex" expression="com.yonyoucloud.uretail.*"/><context:exclude-filter type="regex" expression="com.yonyou.ucf.mdd.core.file.oss.*"/><context:exclude-filter type="regex" expression="com.yonyou.ucf.mdd.poi.*"/><context:exclude-filter type="regex" expression="com.yonyou.ucf.mdd.conf.*" /><context:exclude-filter type="regex" expression="com.yonyou.ucf.mdd.dao.*" /><context:exclude-filter type="regex" expression="com.yonyou.ucf.mdd.rule.context.*" /><context:exclude-filter type="regex" expression="com.yonyou.ucf.mdd.uimeta.context.*" /></context:component-scan>
咱们几个项目的表现不同,就是因为base-package的配置顺序不同导致的。导致会使用不同的全局异常处理器
