背景
  1. 推荐阅读Spring的event的基本使用过程与理解
  2. 测试当线程池中的任务发生异常(RuntimeException)不捕获与@Transactional关系
过程
  • 测试事务(入口处没有注解@Transactional)
  1. 测试逻辑。业务逻辑代码先执行了数据库的更新操作,后续逻辑发生空指针异常。
  2. 数据库情况
  3. 代码情况
    监听代码情况
@EventListener
    @Async("AsyncTaskExecutor")
    public void handleMyEvent(MyEvent myEvent) {
        System.out.println("线程:" + Thread.currentThread().getName() + "执行了MyEvent事件");

        // 具体的业务逻辑过程
        this.eventService.handleEvent(myEvent.getAddr(), myEvent.getTime(), myEvent.getOperator());
    }

具体业务逻辑代码。
studentService.selectByName()和studentService.update()都添加了@Transactional注解。异常是RuntimeException,因此Spring是能够感知到的。

@Override
    public void handleEvent(String addr, Date time, String operator) {
        System.out.println("event: 模拟具体的业务逻辑处理过程, 这里单纯地打印出来即可");
        System.out.println("myEvent.addr: " + addr);
        System.out.println("myEvent.time: " + time);
        System.out.println("myEvent.operator: " + operator);
        Student sara = studentService.selectByName("sara");
        sara.setName("Ted");
        studentService.update(sara);
        String str = null;
        System.out.println(str.equals("null"));

    }
  1. 测试结果(未回滚,数据被修改 了)
  • 测试事务(入口处有注解@Transactional),其他为原来初始情况。
    监听代码
@EventListener
    @Async("AsyncTaskExecutor")
    @Transactional
    public void handleMyEvent(MyEvent myEvent) {
        System.out.println("线程:" + Thread.currentThread().getName() + "执行了MyEvent事件");

        // 具体的业务逻辑过程
        this.eventService.handleEvent(myEvent.getAddr(), myEvent.getTime(), myEvent.getOperator());
    }

测试结果(回滚了,没有修改成功)

spring 线程 spring 线程池 事务_线程池

  • 测试事务(入口处没有注解@Transactional,业务逻辑入口处有@Transactional ),其他为原来初始情况。
@EventListener
    @Async("AsyncTaskExecutor")
    public void handleMyEvent(MyEvent myEvent) {
        System.out.println("线程:" + Thread.currentThread().getName() + "执行了MyEvent事件");

        // 具体的业务逻辑过程
        this.eventService.handleEvent(myEvent.getAddr(), myEvent.getTime(), myEvent.getOperator());
    }
@Override
    @Transactional
    public void handleEvent(String addr, Date time, String operator) {
        System.out.println("event: 模拟具体的业务逻辑处理过程, 这里单纯地打印出来即可");
        System.out.println("myEvent.addr: " + addr);
        System.out.println("myEvent.time: " + time);
        System.out.println("myEvent.operator: " + operator);
        Student sara = studentService.selectByName("sara");
        System.out.println(sara.getName());
        sara.setName("Ted");
        studentService.update(sara);
        String str = null;
        System.out.println(str.equals("null"));

    }

测试结果(事务回滚)

spring 线程 spring 线程池 事务_事务_02

  • 测试事务(入口处有注解@Transactional,业务逻辑入口处有@Transactional ,studentService.update(sara)@Transactional去掉)。
@EventListener
    @Async("AsyncTaskExecutor")
    public void handleMyEvent(MyEvent myEvent) {
        System.out.println("线程:" + Thread.currentThread().getName() + "执行了MyEvent事件");

        // 具体的业务逻辑过程
        this.eventService.handleEvent(myEvent.getAddr(), myEvent.getTime(), myEvent.getOperator());
    }
@Override
    @Transactional
    public void handleEvent(String addr, Date time, String operator) {
        System.out.println("event: 模拟具体的业务逻辑处理过程, 这里单纯地打印出来即可");
        System.out.println("myEvent.addr: " + addr);
        System.out.println("myEvent.time: " + time);
        System.out.println("myEvent.operator: " + operator);
        Student sara = studentService.selectByName("sara");
        System.out.println(sara.getName());
        sara.setName("Ted");
        // 此方法没有@Transactional
        studentService.update(sara);
        String str = null;
        System.out.println(str.equals("null"));

    }

测试结果(事务回滚)

spring 线程 spring 线程池 事务_事务_02

小结
  • Spring的线程池用在Event中,如果线程池中的线程在处理业务逻辑的过程中,发生了RuntimeExcepion时,与Spring注解@Transactional用在的哪个层级,然后导致事务会回滚,有非常大的关系。这个demo实验有很好的参考价值
  • 注意@Transactional默认回滚的异常级别是RuntimeExcepion,而不是Excption
  • @Transactional。Spring默认的传播行为REQUIRED,业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务。