使用@ControllerAdvice完成全局异常统一处理

       在spring 3.2中,新增了@ControllerAdvice,@RestControllerAdvice 注解,可以用于定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping中。参考帮助文档@RestControllerAdvice 是组件注解,他使得其实现类能够被classpath扫描自动发现,如果应用是通过MVC命令空间或MVC Java编程方式配置,那么该特性默认是自动开启的

      @RestControllerAdvice相当于Controller的切面,对异常统一处理,定制,这样更好返回给前端。

       本文介绍使用@RestControllerAdvice和@ExceptionHandler完成全局异常统一处理。

       之前在项目中,Controller层里面经常可以看到如下的代码:

@RestController("/ui/test")
public class TestController {

	@Autowired
	TestService testService;

	private static final Logger logger = LoggerFactory.getLogger(TestController.class);
	
	@GetMapping("/query")
	public BaseResponse<List<TestEntity>> query(ViewForm viewForm){
		BaseResponse<List<TestEntity>> response = new BaseResponse<>();
		try {
			List<TestEntity> testEntity = testService.queryTestByPage(viewForm);
			response.setData(testEntity);
		} catch (Exception e) {
			response.setCode(ErrorCodeEnum.OEFD_GRADE_QUERY_ERROR.getCode());
			response.setMsg(ErrorCodeEnum.OEFD_GRADE_QUERY_ERROR.getMessage());
			//记录日志
			logger.error("query grade error. ", e);
		}
		return response;
	}

	@PostMapping("/save")
	public BaseResponse<TestEntity> save(@RequestBody @Valid ViewForm viewForm){
		
		BaseResponse<TestEntity> response = new BaseResponse<>();
		try {
			TestEntity testEntity = testService.saveRegion(viewForm);
			response.setData(testEntity);
		}catch(BaseRuntimeException baseRuntimeException){
			response.setCode(e1.getCode());
			response.setMsg(e1.getMessage());
			logger.error("save grade params error. ", baseRuntimeException);
		}catch (Exception e) {
			response.setCode(ErrorCodeEnum.OEFD_GRADE_SAVE_ERROR.getCode());
			response.setMsg(ErrorCodeEnum.OEFD_GRADE_SAVE_ERROR.getMessage());
			logger.error("save grade error. ", e);
		}
		return response;
	}
}

       可以看到,在Controller层都要进行异常的捕获,然后根据异常信息,给前段返回相应的提示,像save方法捕获了两个异常,其中对于BaseRuntimeException是应用里面自定义的异常,比如有一个必须要填写的字段为空,那么在testService里面校验后,会抛出一个BaseRuntimeException的异常,然后,在Controller层进行捕获,进行相应的处理,而Exception用来捕获未知的异常,在运行时可能发生的异常都将在Exception中被捕获。

        对于Controller层来说,每一个方法几乎都要进行相同的操作,这样看就显得代码冗余度较高,基本只有调用testService的方法是有用的,其他的都是冗余的代码,使得代码整体看上去不美观,且不好维护。

        下面就使用@RestControllerAdvice和@ExceptionHandler完成对全局异常处理的改造。

         一:IErrorCode接口

public interface IErrorCode {
    /**
     * 获取错误码
     * @return
     */
    String getCode();

    /**
     * 获取错误信息
     * @return
     */
    String getMessage();
    
}

       此接口为统一的错误码接口,业务中可以按模块或按业务自定义枚举错误码,实现该接口,UserErrorCode、CommonErrorCode,内部定义相应的错误码信息。

       二:IErrorCode接口的具体实现:

public enum CommonErrorCode implements IErrorCode {
	//长度超过限制
    LENGTH_EXCEEDS_THE_LIMIT("100010", "errorCode.0x2c130310.description"),
    //请求参数错误
    REQUEST_PARAM_ERROR("100011", "errorCode.0x2c130311.description"),
    //用户id为空
    USER_ID_IS_EMPTY("100012", "errorCode.0x2c130312.description"),
    //无该用户
    USER_IS_NOT_FOUND("100013", "errorCode.0x2c130313.description"),
    // 发送消息失败
    SEND_MSG_ERROR("100014", "errorCode.0x2c130314.description"),
    // SQL执行异常
    SQL_EXCEPTION("10001a","errorCode.0x2c13031a.description"),
    // 数据库连接异常
    DATASOURCE_CONNECTION_ERROR("10001b", "errorCode.0x2c13031b.description"),

    ERR_GET_FILE_ADDR_ERROR("10001c", "file address get error"),
    ERR_UPLOAD_FILE_ERROR("10001d", "upload file error");

    private String code;
    private String message;

    CommonErrorCode(String code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public String getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }
}
public enum FileErrorCode implements IErrorCode {

    //文件上传
    File_UPLOAD_ERROR("100100", "errorCode.0x2c130200.description"),
    //目录创建错误
    DIRECTORY_CREATE_ERROR("100101", "errorCode.0x2c130201.description"),
    //模板不存在
    TEMPLATE_NOT_EXISTS("100102", "errorCode.0x2c130202.description"),
    //模板下载错误
    TEMPLATE_DOWNLOAD_ERROR("100103", "errorCode.0x2c130203.description"),
    //模板导入非法
    TEMPLATE_IMPORT_ILLEGAL("100104", "errorCode.0x2c130204.description"),
    TEMPLATE_IMPORT_OVVERSIZE("100105", "errorCode.0x2c130205.description"),
    //模板导入错误
    TEMPLATE_IMPORT_ERROR("100106", "errorCode.0x2c130206.description"),
    TEMPLATE_IMPORT_EMPTY("100107", "errorCode.0x2c130207.description"),
    TEMPLATE_FORMAT_ERROR("100108", "errorCode.0x2c130208.description"),
    TEMPLATE_EXAMPLE_LINE("100109", "errorCode.0x2c130209.description"),
    TEMPLATE_PROCESS_ERROR("100110", "errorCode.0x2c130210.description"),
    ;

    private String code;
    private String message;

    FileErrorCode(String code, String msg) {
        this.code = code;
        this.message = msg;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }
}

       这里说一下,错误码可以按照 服务 + 业务 + 顺序 进行唯一指定一个错误码,可以将系统中常见的错误码进行抽离,然后针对每个服务在进行指定特定的错误码的方式,对于每一个定义错误码业务类都要实现IErrorCode接口,这样便于后面的处理。

      三:自定义异常类----BaseRuntimeException

public class BaseRuntimeException extends RuntimeException{

    private String code;
    private Object[] params;

    public BaseRuntimeException(String message){
        super(message);
    }

    public BaseRuntimeException(String message, Throwable cause){
        super(message, cause);
    }

    public BaseRuntimeException(String code,String message){
        super(message);
        this.code = code;
    }

    public BaseRuntimeException(Throwable cause, String code,String message){
        super(message, cause);
        this.code = code;
    }

    public BaseRuntimeException(String code, String message,Object... params){
        super(message);
        this.code = code;
        this.params = params;
    }

    public BaseRuntimeException(IErrorCode errorCode){
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
    }

    public BaseRuntimeException(IErrorCode errorCode, Object[] args){
        super(errorCode.getMessage());
        this.code = errorCode.getCode();
        if (null == args) {
        } else {
            this.params = args.clone();
        }
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Object[] getParams() {
        if (null == params) {
            return null;
        } else {
            return  params.clone();
        }
    }

    public void setParams(Object[] params) {
        if (null == params) {
            this.params = null;
        } else {
            this.params =  params.clone();
        }
    }
}

       四:全局异常处理类---------GlobalExceptionHandler

@RestControllerAdvice
public class GlobalExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler
    public BaseResponse handle(BaseRuntimeException exception) {
        //errorCode处理
        String code = ResponseUtil.formatErrorCode(exception.getCode());

        //记录错误日志
        logger.error(LogFormatter.toLog(code, exception.getMessage()), exception);

        //进行统一格式的错误返回
        return BaseResponseBuilder.createResponse(code, exception.getMessage(), exception.getParams());
    }

    /**
     * 其他没有处理的错误
     *
     * @param e
     * @return
     */
    @ExceptionHandler
    public BaseResponse<Object> handle(Exception e) {
        //记录日志
        logger.error(LogFormatter.toLog(DefaultErrorCode.ERROR), e);
        //DefaultErrorCode.ERROR为未归类错误码
        return BaseResponseBuilder.createResponse(DefaultErrorCode.ERROR);
    }
}

        在GlobalExceptionHandler类中捕获了BaseRuntimeException和Exception异常,如果发生BaseRuntimeException的异常,会被方法一进行捕获,其他的异常都会被方法二给捕获,如果想要捕获其他的具体的异常,可以在这个类中在进行定义就行了。

        五: 具体使用

@RestController("/ui/test")
public class TestController {

	@Autowired
	TestService testService;
	
	@GetMapping("/query")
	public BaseResponse<List<TestEntity>> query(ViewForm viewForm){
		BaseResponse<List<TestEntity>> response = new BaseResponse<>();
		List<TestEntity> testEntity = gradeService.queryTestByPage(viewForm);
		response.setData(testEntity);

		return response;
	}

	@PostMapping("/save")
	public BaseResponse<TestEntity> save(@RequestBody @Valid ViewForm viewForm){
		BaseResponse<TestEntity> response = new BaseResponse<>();
		TestEntity testEntity = gradeService.saveRegion(viewForm);
		response.setData(testEntity);
		
		return response;
	}
}

@Service
public class TestServiceImpl implements TestService{

	@Override
	public List<TestEntity> queryTestByPage(ViewForm viewForm) { 
		if(viewForm == null || StringUtils.isEmpty(view.name)){
			throw new BaseRuntimeException(CommonErrorCode.REQUEST_PARAM_ERROR)
		}

		//xxx进行业务处理
	}
}

        当检测失败时,TestServiceImpl 会直接抛出一个BaseRuntimeException的异常,然后就会被GlobalExceptionHandler类中的方法进行捕获,根据CommonErrorCode.REQUEST_PARAM_ERROR里面的定义的错误码,在前端会有想要的字段与其对应,完成非常友好的提示工作。

       对于抛出一个异常,不要觉得就是不好的,这样可以让用户去检查自己的操作是否正确,并给出相应的提示工作。

       到此关于全局异常统一处理就完成了,对于这个功能点,能够降低维护成本,使代码看起来更加的简洁,感兴趣的可以了解一下其具体的实现。

 

 
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,您想了解关于使用 @ExceptionHandler + @ControllerAdvice +枚举实现全局异常统一处理的内容,这是一个常用的方案,可以实现全局异常的捕获和处理,让程序更加健壮和稳定。 @ExceptionHandler注解用于标记一个方法,该方法用于处理指定的异常类型。@ControllerAdvice注解用于标记一个类,该类用于处理全局异常。枚举则可以用于定义异常类型,方便异常处理时的统一管理。 在实现全局异常处理时,我们可以通过 @ExceptionHandler 注解来捕获指定的异常类型,并在方法中进行相应的处理。@ControllerAdvice注解可以让我们在一个类中定义多个 @ExceptionHandler方法,用于处理不同类型的异常。同时,我们也可以使用枚举来定义不同的异常类型,方便管理和调用。 下面是一个简单的示例,演示如何使用 @ExceptionHandler + @ControllerAdvice +枚举实现全局异常统一处理: ```java @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = BusinessException.class) @ResponseBody public ResultVO handleBusinessException(BusinessException e) { return ResultVO.error(e.getCode(), e.getMessage()); } } public enum ExceptionEnum { PARAMETER_ERROR(1001, "参数错误"), DATA_NOT_FOUND(1002, "数据不存在"), SYSTEM_ERROR(5000, "系统错误"); private final int code; private final String message; ExceptionEnum(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public String getMessage() { return message; } } public class BusinessException extends RuntimeException { private final int code; public BusinessException(int code, String message) { super(message); this.code = code; } public BusinessException(ExceptionEnum exceptionEnum) { super(exceptionEnum.getMessage()); this.code = exceptionEnum.getCode(); } public int getCode() { return code; } } ``` 在上面的示例中,GlobalExceptionHandler类标记了@ControllerAdvice注解,用于全局异常处理。其中,handleBusinessException方法用于处理BusinessException异常,返回一个ResultVO对象,其中包含错误码和错误信息。 BusinessException则是一个自定义的异常类,它包含一个code属性和一个message属性,用于表示异常的错误码和错误信息。同时,它还提供了一个构造方法,可以根据ExceptionEnum来构造一个BusinessException对象。 ExceptionEnum则是一个枚举类,包含了不同的异常类型,每个异常类型都有一个对应的错误码和错误信息。 在实际开发中,我们可以根据实际需求来定义不同的异常类型和错误码,以便更好地管理和调用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值