public interface Service{


  //业务方法

  doBusiness();

  //数据库更新方法

  doBusinessTX();

}

@Service

public class ServiceImpl implements Service,ApplicationContextAware{

  //自己注入自己

  @Autowired

  private Service ServiceProxy;

  //或者使用Aware接口获取BeanFactory获取当前对象事务代理对象

  @Override

  public void setApplicationContext(ApplicationContext ctx) throws BeansException{

    this.Service = ctx.getBean(Service.class);

  }

  @Transactional(rollback=RuntimeException.class)//默认RuntimeException和虚拟机Error回滚

  public void doBusinessTX(){

    数据库更新 可抛出RuntimeException 自动触发数据库回滚

    return;

  }

  public void operateRedis(){

    //redisTemplate.eval 涉及多个KEY操作使用LUA脚本

  }

  public void operateRedisRollback(){

    //redisTemplate.eval 执行LUA脚本 多个KEY同时操作 原子执行 回退redis修改

  }

  public void operateMQ() throws MQException{

    //发送MQ 捕捉RuntimeException 转换为MQException再抛出 其他异常转换为MQException

  }

  @Override

  public void doBusiness(){

    try{

      this.operateRedis();//先操作redis

      this.ServiceProxy.doBusinessTX();//再操作数据库,代理对象调用事务方法 方法结束事务提交

      this.operateMQ();//最后发送MQ 发送失败需重发 重发仍失败抛出MQException

    }catch(RuntimeException e){

      //运行时异常 事务方法抛出后 数据库已回滚 需手动回滚redis

      this.operateRedisRollback();

    }catch(MQException me){

      //MQException 后发MQ,如果时先发MQ,则可能涉及数据库事务失败再发送撤销MQ,可能触发MQ关联系统回滚,增加复杂性,如先发MQ最后确认,则数据库事务期间占用MQ链接而不确认,实际效果同后发MQ,如后发MQ重试仍发送失败,通常业务状态置为异常,需手动冲正或异步定时任务处理或延迟发送,如果还有文件服务器的操作,一般不能将耗时IO操作放在数据库事务中,可异步调起文件服务操作,并可使用类似信号量限制IO。

    }finally{


    }

  }

}