Spring的TransactionEventListener

Spring的TransactionEventListener

  1. 问题背景:
    在之前的项目中,有遇到这样一个使用场景:
    在一个TransactionA中,我们先向数据库中插入了一条数据EntityA,之后起了一个线程池,而在线程池中我们需要读取数据库中EntityA。这时候就有个问题了,由于我们知道Transaction是与线程绑定的(ThreadLocal), 那么在线程池中的其他线程,我们可能存在一个情况就是,TransactionA还没提交或处于提交的过程中,那么此时我们是对于EntityA是不可见的。于是就需要将提交到线程的过程与TransactionA分开。在工程中,我们是使用了一种比较丑的方法,也就将待处理的EntityA id当做返回值返回给上层(事务外),然后通过上层来进行调用。但是通过这个流程过耦合了,并不是一种优美的实现方式。

  2. 解决方案:
    2.1 TransactionEventListener
    这几天在浏览Spring Reference的时候,看到了一种方法能够很有效的方式。
    在Spring4.2+,有一种较TransactionEventListener的方式,能够控制在事务的时候Event事件的处理方式。
    我们知道,Spring的发布订阅模型实际上并不是异步的,而是同步的来将代码进行解耦。而TransactionEventListener仍是通过这种方式,只不过加入了回调的方式来解决,这样就能够在事务进行Commited,Rollback…等的时候才会去进行Event的处理。
    代码如下:

    @Service(“fooService”)
    public class FooServiceImpl implements FooService {
    private static final Logger LOGGER = Logger.getLogger(FooServiceImpl.class);
    @Override
    public void insertFoo(Foo foo) throws MyTransactionException {
    LOGGER.info("[fooService] start insert foo");
    ApplicationEventPublisher eventPublisher = EventPublisher.getApplicationEventPublisher();
    if (null != eventPublisher) {
    eventPublisher.publishEvent(new MyTransactionEvent(“test”, this));
    }
    LOGGER.info("[fooServive] finish insert foo");
    }
    }

public class MyTransactionEvent extends ApplicationEvent {

private String name;

public MyTransactionEvent(String name, Object source) {
    super(source);
    this.name = name;
}

public String getName() {
    return this.name;
}

}

@Component
public class MyTransactionListener {

    private static final Logger LOGGER = Logger.getLogger(MyTransactionListener.class);

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void hanldeOrderCreatedEvent(MyTransactionEvent event) {
        LOGGER.info("transactionEventListener start");
//        do transaction event
        LOGGER.info("event : " + event.getName());
//        finish transaction event
        LOGGER.info("transactionEventListener finish");
    }
}```
输出结果:
[DEBUG] 2018-06-01 11:23:50,165 [main]  jdbc.datasource.DataSourceTransactionManager    (DataSourceTransactionManager.java:248) -Acquired Connection [1794768050, URL=jdbc:mysql://127.0.0.1:3306/test, UserName=root@localhost, MySQL Connector Java] for JDBC transaction
[DEBUG] 2018-06-01 11:23:50,172 [main]  jdbc.datasource.DataSourceTransactionManager    (DataSourceTransactionManager.java:265) -Switching JDBC Connection [1794768050, URL=jdbc:mysql://127.0.0.1:3306/test, UserName=root@localhost, MySQL Connector Java] to manual commit
[INFO]  2018-06-01 11:23:50,173 [main]  transaction.declarative.FooServiceImpl  (FooServiceImpl.java:36)    -[fooService] start insert foo
[DEBUG] 2018-06-01 11:23:50,174 [main]  factory.support.DefaultListableBeanFactory  (AbstractBeanFactory.java:251)  -Returning cached instance of singleton bean 'fooService'
[DEBUG] 2018-06-01 11:23:50,175 [main]  jdbc.datasource.DataSourceTransactionManager    (AbstractPlatformTransactionManager.java:476)   -Participating in existing transaction
[INFO]  2018-06-01 11:23:50,175 [main]  transaction.declarative.FooServiceImpl  (FooServiceImpl.java:43)    -[fooServive] finish insert foo
[DEBUG] 2018-06-01 11:23:50,175 [main]  jdbc.datasource.DataSourceTransactionManager    (AbstractPlatformTransactionManager.java:759)   -Initiating transaction commit
[DEBUG] 2018-06-01 11:23:50,178 [main]  jdbc.datasource.DataSourceTransactionManager    (DataSourceTransactionManager.java:310) -Committing JDBC transaction on Connection [1794768050, URL=jdbc:mysql://127.0.0.1:3306/test, UserName=root@localhost, MySQL Connector Java]
[DEBUG] 2018-06-01 11:23:50,184 [main]  factory.support.DefaultListableBeanFactory  (AbstractBeanFactory.java:251)  -Returning cached instance of singleton bean 'myTransactionListener'
[INFO]  2018-06-01 11:23:50,185 [main]  transaction.event.MyTransactionListener (MyTransactionListener.java:21) -transactionEventListener start
[INFO]  2018-06-01 11:23:50,185 [main]  transaction.event.MyTransactionListener (MyTransactionListener.java:23) -event : test
[INFO]  2018-06-01 11:23:50,185 [main]  transaction.event.MyTransactionListener (MyTransactionListener.java:25) -transactionEventListener finish
[DEBUG] 2018-06-01 11:23:50,192 [main]  jdbc.datasource.DataSourceTransactionManager    (DataSourceTransactionManager.java:368) -Releasing JDBC Connection [1794768050, URL=jdbc:mysql://127.0.0.1:3306/test, UserName=root@localhost, MySQL Connector Java] after transaction
[DEBUG] 2018-06-01 11:23:50,193 [main]  jdbc.datasource.DataSourceUtils (DataSourceUtils.java:329)  -Returning JDBC Connection to DataSource

``
需要注意的是,如果想使用这个注解,需要在配置文件中添加:

tx:annotation-driven/
由于只有在AnnotationDrivenBeanDefinitionParser的parse方法中,我们才会将
TransactionalEventListenerFactory加入到容器中。

/**
 * Parses the {@code <tx:annotation-driven/>} tag. Will
 * {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
 * with the container as necessary.
 */
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
    registerTransactionalEventListenerFactory(parserContext);
    String mode = element.getAttribute("mode");
    if ("aspectj".equals(mode)) {
        // mode="aspectj"
        registerTransactionAspect(element, parserContext);
    }
    else {
        // mode="proxy"
        AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
    }
    return null;
}

对于注解@TransactionEventListener来讲,有一个属性fallbackExecutor,其默认值为false。如果将其设为true,则无论当前的场景是否处在事务上下文中,均能够执行。

2.2 TransactionSynchronizationManager.registerSynchronization
第二种解决方法便是通过手动的来注册回调来实现的。
我们可与利用博客Spring Event 事件中的事务控制的方式来进行。
比如:

@EventListener
public void afterRegisterSendMail(MessageEvent event) {
    // Spring 4.2 之前
 
    TransactionSynchronizationManager.registerSynchronization(
            new TransactionSynchronizationAdapter() {
                @Override
                public void afterCommit() {
                    internalSendMailNotification(event);
                }
            });
}``
上面的代码将在事务提交后执行.如果在非事务context中将抛出java.lang.IllegalStateException: Transaction synchronization is not active,
@EventListener
    public void afterRegisterSendMail(MessageEvent event) {
        // Spring 4.2 之前
     
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
     
            TransactionSynchronizationManager.registerSynchronization(
                    new TransactionSynchronizationAdapter() {
                        @Override
                        public void afterCommit() {
                            mailService.send(event);
                        }
                    });
        } else {
            mailService.send(event);
        }
    }
    ```

通过上面这种进行人为的判断后,我们便可以兼容是否在事务上下文而不用抛出异常。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值