美文网首页
Spring Boot 异常处理思路

Spring Boot 异常处理思路

作者: 东本三月 | 来源:发表于2019-03-21 20:26 被阅读0次

1.需求/目的

  • 快速抛出异常
  • 异常统一处理
  • 接口返回异常内容

2.使用环境

  • spring boot 2.0.3
  • maven
  • log4j

3. 异常与提示信息枚举

  • 系统的常用异常和提示信息都以枚举的形式定义,这里只定义了一些最基础的.其他信息可以创建新的枚举类进行定义.
public enum MsgEnum implements MsgEnumInterface {

    SUCCESS("1","请求成功!"),
    UNKNOWN_ERROR("-1","未知异常!"),
    PARA_ERROR("-100","参数错误!");

    String code=null;
    String message=null;
    private MsgEnum(String code, String message){
        this.code=code;
        this.message=message;
    }

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

    @Override
    public String getMessage() {
        return this.message;
    }

  • 所有异常与提示的枚举需要有一个接口,方便其他类进行识别与使用.
public interface MsgEnumInterface {
     String getCode();
     String getMessage();
}

4.自定义异常类

  • 首先创建一个自定义异常类的父类,方便进行扩展修改,这是非必要的,也可以不定义.
    这里添加了两条新的属性,code为消息代码,data为消息的附件数据(非必要).
    异常类定义多个构造方法,支持不同参数,同时允许使用异常信息枚举创建异常
public class BasicException extends RuntimeException {

    public final static  String DEFAULT_CODE="-1";
    String code;
    Object data;
    public BasicException() {
        super();
    }

    public BasicException(String message) {
        super(message);
        code=DEFAULT_CODE;
    }

    public BasicException(String code, String message){
        this(message);
        this.code=code;
    }
    public BasicException(String code, String message, Object data){
        this(message);
        this.code=code;
        this.data=data;
    }

    //通过消息枚举创建异常
    public BasicException(MsgEnumInterface msgEnum){
        this(msgEnum.getMessage());
        this.code=msgEnum.getCode();
    }

    public String getCode() {
        return code;
    }

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

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }
    
}
  • 创建两个异常类MsgException,ErrorException
    MsgException表示一些业务上的错误信息,如:已提交申请,不能再次提交;密码不能小于13位字符等等
    ErrorException表示执行过程中出现的一些错误,如:数据可能为空,进行捕获要抛出;sql执行失败等
public class ErrorException extends BasicException {
    public ErrorException() {
        super();
    }

    public ErrorException(String message) {
        super(message);
        this.code=DEFAULT_CODE;
    }
    
    public ErrorException(String code, String message){
        this(message);
        this.code=code;
    }
    public ErrorException(String code, String message, Object data){
        this(message);
        this.code=code;
        this.data=data;
    }
}
public class MsgException extends BasicException {
    public MsgException() {
        super();
    }

    public MsgException(String message) {
        super(message);
        code=DEFAULT_CODE;
    }
    
    public MsgException(String code,String message){
        this(message);
        this.code=code;
    }
    public MsgException(String code,String message,Object data){
        this(message);
        this.code=code;
        this.data=data;
    }
}

5.异常抛出工具类

-便捷的抛出异常


/**
 * 用于快捷的抛出异常
 * @author authstr
 */
public class Assert {
     /**
      * 为false时抛出一个"未知错误"的异常
     * @param exp 为false抛出异常
     * @time 2018年9月17日10:10:33
     * @author authstr
     */
    public static void isTrue(boolean exp) {
            Assert.isTrue(exp, "执行过程中出现未知错误!");
        }

        /**
         *  为false时抛出一个指定message的异常
         * @param exp   为false抛出异常
         * @param message 该异常要显示的信息
         * @time 2018年9月17日10:13:05
         * @author authstr
         */
        public static void isTrue(boolean exp, String message) {
            Assert.isTrue(exp, message, false);
        }
        
        public static void isTrue(boolean exp, String code,String message) {
            Assert.isTrue(exp, code, message,null,false);
        }

        /**
         * 为false时抛出一个指定message的消息异常或者错误异常
         * @param exp 为false抛出异常
         * @param message 该异常要显示的信息
         * @param isInside 该异常是否为错误异常
         * @time 2018年9月17日10:13:17
         * @author authstr
         */
        public static void isTrue(boolean exp, String message, boolean isInside) {
             Assert.isTrue(exp, null, message,null,isInside);
        }
        
        
        /**
         * 为false时抛出一个指定信息的消息异常或者错误异常
         * @param exp 为false抛出异常
         * @param message 异常的详细说明
         * @param data  该异常要附带的数据信息
         * @param isInside 该异常是否为错误异常
         * @time 2018年9月26日10:11:19
         * @author authstr
         */
        public static void isTrue(boolean exp, String code, String message,Object data,boolean isInside){
             if (!exp) {
                if (!isInside) {
                    throw new MsgException(code,message,data);
                }else{
                    throw new ErrorException(code,message,data);
                }
             }
        }

        /**
         * 对象为空时,抛出一个异常
         * @param o
         * @time 2019年3月21日20:07:56
         */
        public static void notNull(Object o){
            if(o==null){
                throw new ErrorException("系统未知异常:null");
            }
        }
}

6.控制层异常捕获

  • 创建一个控制层,作为所有接口型控制层的父类,使用@ExceptionHandler(value={Exception.class})注解来捕获异常
  • 局部异常处理 @Controller + @ExceptionHandler
  • 全局异常处理 @ControllerAdvice + @ExceptionHandler
  • 当前没有对页面跳转相关异常进行处理,因此不能使用全局异常处理
  • MsgException异常需要在接口返回异常内容,提示调用者,ErrorException不需要告知调用者,返回"未知异常"即可
  • 此类当前不完善,其他的异常没有进行差异化处理(暂时没有对应的需求)
/**
 * 控制层的父类,主要对控制层的异常进行捕获,通过接口返回或者进行其他处理
 */
@Component
public class AbstractController {
    protected Logger log = LogManager.getLogger(this.getClass());
    @ExceptionHandler(value={Exception.class})
    @ResponseBody
    public Map exceptionHandler(Exception ex) {
        HashMap<String, Object> model = new HashMap<String, Object>();
        //如果是消息型异常
        if (ex instanceof MsgException) {
            MsgException msg=(MsgException) ex;

            //尝试从异常获取错误代码,没有则使用默认的错误代码
            if(StringUtils.hasText(msg.getCode())){
                  model.put(ControllerConstant.CODE, msg.getCode());
            }else{
                  model.put(ControllerConstant.CODE,MsgEnum.UNKNOWN_ERROR.getCode());
            }

            //尝试从异常获取错误说明,没有则使用默认的错误说明
            if(StringUtils.hasText(msg.getMessage())){
                model.put(ControllerConstant.MSG, msg.getMessage());
            }else{
                model.put(ControllerConstant.MSG,MsgEnum.UNKNOWN_ERROR.getMessage());
            }

            //获取异常详细数据,如果有,进行返回
            if(msg.getData()!=null){
                model.put(ControllerConstant.DATA, msg.getData());
            }

            
        } else if(ex instanceof ErrorException){
            //出现错误型异常,在返回值显示未知异常,具体错误消息进行记录和打印
            model.put(ControllerConstant.CODE,MsgEnum.UNKNOWN_ERROR.getCode());
            model.put(ControllerConstant.MSG,MsgEnum.UNKNOWN_ERROR.getMessage());
            ex.printStackTrace();
            log.error("系统执行出现异常:" + ex.getMessage());
        }
        else {
            model.put(ControllerConstant.CODE,MsgEnum.UNKNOWN_ERROR.getCode());
            model.put(ControllerConstant.MSG,MsgEnum.UNKNOWN_ERROR.getMessage());
            ex.printStackTrace();
            this.log.error( "系统出现未知异常 :" + ex.getMessage());
        }
        return model;
    }

    public Map success() {
        HashMap<String, String> map = new HashMap<String, String>();
        map.put(MsgEnum.SUCCESS.getCode(),MsgEnum.SUCCESS.getMessage());
        return map;
    }
}

7.使用示例

  • 控制层使用
@RestController
@RequestMapping("user")
public class UserController extends AbstractController {
        @Autowired
        UserService userService;
        
        @RequestMapping("query")
         public Map query(QueryCommonPage query, HttpServletRequest request) {
            Map m = super.success();
            RequestPara para=new RequestPara(request);
            m.put("page", userService.query(query,para));
            return m;
        }

        @RequestMapping("save")
        public Map save(BaseUser user) {
            Map m = super.success();
            m.put("data", userService.save(user));
            return m;
        }

}
  • 服务层使用
@Service
public class UserServiceImpl extends AbstractService implements UserService{
    @Autowired
    private UserDao userDao;
    @Override
    public Page query(QueryCommonPage query, RequestPara para){
        return userDao.query(query,para);
    }

    @Transactional
    @Override
    public String save(BaseUser user){
        Assert.isTrue(StringUtils.hasText(user.getUsername()),"用户名不能为空");
        Assert.isTrue(StringUtils.hasText(user.getPassword()),"密码不能为空");
        Integer id=(Integer) super.save(user);
        //密码md5加密后,用数据自身id作为盐,再次加密
        String md5Password= null;
        try {
            md5Password = Md5Salt.sec(String.valueOf(id), DigestUtils.md5Hex(user.getPassword()));
        } catch (Exception e) {
            throw new ErrorException("-3","加密出错!");//异常提示实际应写在枚举里
        }
        BaseUser afterUser=super.get(BaseUser.class,id);
        Assert.notNull(afterUser);
        afterUser.setPassword(md5Password);
        super.updata(afterUser);
        return "保存成功";
    }
}

相关文章

网友评论

      本文标题:Spring Boot 异常处理思路

      本文链接:https://www.haomeiwen.com/subject/dhrcvqtx.html