实际springMVC项目中自定义异常、spring事务与异常的简单应用

一、异常相关知识:

  1. 非运行时异常(Checked Exception)
    Java中凡是继承自Exception但不是继承自RuntimeException的类都是非运行时异常。

  2. 运行时异常(Runtime Exception/Unchecked Exception)
    RuntimeException类直接继承自Exception类,称为运行时异常。Java中所有的运行时异常都直接或间接的继承自RuntimeException。

3、Java中所有的异常类都直接或间接的继承自Exception。

4、spring事务回滚机制只处理运行时异常,发生非运行时异常则不会回滚操作。

二、自定义异常的大体思路

1、通过上面异常相关知识介绍,在实际的springmvc项目中自定义异常采用继承RuntimeException类的方式;

2、为了防止注解了spring事务的service方法中发生非运行其异常,导致的事务不回滚操作,service层触发事务相关代码主动用try{}catch(){}包裹,最后的catch捕获Exception异常(即包含了运行期和非运行期异常),
catch内部抛出自定义的运行期异常,以达到把非运行其异常转换为运行期异常的效果,保证所有异常spring事务都执行回滚操作。

三、自定义异常实例

自定义基类异常:

/**
 * 所有秒杀相关异常(运行期异常)
 * spring事务,只接收运行期异常,执行回滚操作
 */
public class SeckillException extends RuntimeException{

    private static final long serialVersionUID = 1L;

    //重载构造函数
    public SeckillException(String message, Throwable cause) {
        super(message, cause);
    }

    //重载构造函数
    public SeckillException(String message) {
        super(message);
    }

}

自定义两个子类异常:

/**
 *  秒杀关闭异常:可能未开启,可能已结束(时间到期,库存为0),可能执行失败等
 *  秒杀异常的子类型异常
 */
public class SeckillClosedException extends SeckillException{

    private static final long serialVersionUID = 1L;

    //重载构造函数
    public SeckillClosedException(String message, Throwable cause) {
        super(message, cause);
    }

    //重载构造函数
    public SeckillClosedException(String message) {
        super(message);
    }
}
/**
 *  重复秒杀异常,秒杀异常的子类型异常
 */
public class RepeatSeckillException extends SeckillException{

    private static final long serialVersionUID = 1L;

    //重载构造函数
    public RepeatSeckillException(String message){
        super(message);
    }

    //重载构造函数
    public RepeatSeckillException(String message,Throwable cause){
        super(message, cause);
    }
}

service层事务方法:

@Transactional
@Override
public SeckillExecution executeSeckill(。。。) throws SeckillException, SeckillClosedException,RepeatSeckillException {

    //将所有操作用try{}catch(){}包裹,然后在catch中抛出运行期异常,以便发生异常时spring事务回滚操作
    try{
        //insert语句并没有触发事务操作,但是inset要保证与update事务的一致性
        int insertCount = successkilledDao.insertSuccessKilled(seckillId, userPhone);
        //重复秒杀
        if(insertCount <= 0){
            throw new RepeatSeckillException(SeckillStateEnum.REPEAT_KILL.getStateInfo());
        }else{

            //减库存
            Date nowTime = new Date();
            //开启事务,获取updateCount值,提交并关闭事务;如果抛出异常则回滚事务
            int updateCount = seckillDao.reduceNumber(seckillId, nowTime);
            //没有更新数据,秒杀结束
            if(updateCount  <= 0){
                throw new SeckillClosedException(SeckillStateEnum.END.getStateInfo());
            }else{
                //秒杀成功,获取当前购买明细实体
                SuccessKilled successKilled = successkilledDao.querySuccessKilledWithSeckill(seckillId, userPhone);
                return new SeckillExecution(seckillId,SeckillStateEnum.SUCCESS,successKilled);
            }
        }

    //将try中抛出的已知异常用已知异常捕获,然后再次抛出
    //为的是避免被最后的用于处理未知异常的Exception e捕获,让调用者获取最为准确的信息
    }catch(SeckillClosedException se){
        logger.error(se.getMessage());
        throw se;
    }catch(RepeatSeckillException re){
        logger.error(re.getMessage());
        throw re;

    //最后捕获所有未知异常(相对于上面两个已知异常),然后再主动抛出自定义运行期异常
    }catch(Exception e){
        logger.error(e.getMessage(),e);
        //将检查(编译期)异常转换为运行期异常,spring事务回滚只负责运行期异常
        throw new SeckillException(SeckillStateEnum.INSERT_ERROR.getStateInfo()+" : "+e.getMessage());
    }
}

上述方式的弊端是,如果业务线比较多,自定义的异常子类也会比较多,完全可以定义为一个通用的UncheckedException异常,如下:

/**
 * @Description:通用的UncheckedException异常,继承RuntimeException,为运行期异常
 */
public class UncheckedException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    /** 错误Key,用于唯一标识错误类型 */
    private String errorCode = null;
    /** 错误信息 */
    private String errorMessage;
    /** 传递给变量的错误值 */
    private Object[] errorParam = null;

    /**
     * 构造函数
     * @param errorCode 异常编码
     */
    public UncheckedException(String errorCode) {
        this.errorCode = errorCode;
    }

    /**
     * 构造函数
     * @param errorCode 异常编码
     * @param errorParam Object[] 异常信息用到的参数
     */
    public UncheckedException(String errorCode, Object[] errorParam) {
        this.errorCode = errorCode;
        this.errorParam = errorParam;
    }

    /**
     * 重载构造函数
     * @param errorCode 异常编码
     * @param errorParam 异常信息用到的参数
     * @param t 异常实例
     */
    public UncheckedException(String errorCode, Object[] errorParam, Throwable t) {
        super(t);
        this.errorCode = errorCode;
        this.errorParam = errorParam;
    }

    /**
     * 重载构造函数
     * @param message 异常信息
     * @param t 异常实例
     */
    public UncheckedException(String message, Throwable t) {
        super(message, t);
        setErrorMessage(message);
    }



    /**
     * 异常编码
     * @return String
     */
    public String getErrorCode() {
        return this.errorCode;
    }

    /**
     * 异常信息用到的参数
     * @return Object[]
     */
    public Object[] getErrorParam() {
        return this.errorParam;
    }

    /**
     * 错误信息
     * 
     * @return
     */
    public String getErrorMessage() {
        return errorMessage;
    }

    /**
     * 错误信息
     * 
     * @param errorMessage
     */
    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

    /**
     * 覆盖方法:getMessage
     * @return String
     */
    @Override
    public String getMessage() {
        if (errorMessage != null) {
            return errorMessage;
        }

        //异常信息以资源文件的形式保存,并且支持国际化,此处通过errorCode去读取国际化异常信息
        if (errorCode != null && !errorCode.trim().equals("")) {
            setErrorMessage(AppLang.getLU().getMessage(errorCode, errorParam,Locale.SIMPLIFIED_CHINESE));
        }

        return getErrorMessage();
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值