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

来源:互联网 发布:为知笔记积分 编辑:程序博客网 时间:2024/04/29 10:08

一、异常相关知识:

  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@Overridepublic 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();    }}
1 0