文章目录
1. 什么是Spring事务监听器?
Spring
事务监听器是一种机制,允许我们在事务的不同阶段(如提交、回滚、开始)执行自定义逻辑。通过事务监听器,我们可以在事务的生命周期中插入一些额外的操作,比如记录日志、发送通知、更新缓存等。
2. 通过TransactionSynchronization 接口实现事务监听器
在
Spring
中,事务监听器的设计主要通过实现
TransactionSynchronization
接口并将其注册到当前事务中来实现。这允许在事务的不同阶段(如提交前、提交后、回滚后等)执行特定的逻辑,从而增强事务处理的灵活性和可控性。
核心组件
- TransactionSynchronization 接口:这是一个监听器接口,用于接收事务同步事件。该接口提供了多个回调方法,允许我们在事务的不同阶段执行操作。
- TransactionSynchronizationManager 类:这是事务同步管理器,负责管理事务同步的回调。
TransactionSynchronization 接口的方法
TransactionSynchronization
接口定义了以下方法:
- void suspend(): 事务挂起时调用。
- void resume(): 事务恢复时调用。
- void flush(): 事务刷新时调用。
- void beforeCommit(boolean readOnly): 事务提交前调用。
- void beforeCompletion(): 事务完成前调用。
- void afterCommit(): 事务提交后调用。
- void afterCompletion(int status): 事务完成后调用。
接下来,用完整的代码给大家展示这些方法的调用时机
还是用上一篇相近的例子,全部代码如下:
配置类
AppConfig
类配置了数据源、
SqlSessionFactory
、
SqlSessionTemplate
和事务管理器。
packagecom.example.demo.configuration;importorg.apache.ibatis.session.SqlSessionFactory;importorg.mybatis.spring.SqlSessionFactoryBean;importorg.mybatis.spring.SqlSessionTemplate;importorg.mybatis.spring.annotation.MapperScan;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.ComponentScan;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.EnableAspectJAutoProxy;importorg.springframework.core.io.support.PathMatchingResourcePatternResolver;importorg.springframework.jdbc.datasource.DataSourceTransactionManager;importorg.springframework.jdbc.datasource.DriverManagerDataSource;importorg.springframework.transaction.PlatformTransactionManager;importorg.springframework.transaction.annotation.EnableTransactionManagement;importjavax.sql.DataSource;@Configuration@ComponentScan(basePackages ="com.example.demo")@MapperScan("com.example.demo.mapper")@EnableAspectJAutoProxy@EnableTransactionManagementpublicclassAppConfig{@BeanpublicDataSourcedataSource(){DriverManagerDataSource dataSource =newDriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
dataSource.setUsername("root");
dataSource.setPassword("password");return dataSource;}@BeanpublicSqlSessionFactorysqlSessionFactory(DataSource dataSource)throwsException{SqlSessionFactoryBean sqlSessionFactoryBean =newSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
sqlSessionFactoryBean.setMapperLocations(newPathMatchingResourcePatternResolver().getResources("classpath*:mybatis/**/*.xml"));return sqlSessionFactoryBean.getObject();}@BeanpublicSqlSessionTemplatesqlSessionTemplate(SqlSessionFactory sqlSessionFactory){returnnewSqlSessionTemplate(sqlSessionFactory);}@BeanpublicPlatformTransactionManagertransactionManager(DataSource dataSource){returnnewDataSourceTransactionManager(dataSource);}}
自定义事务同步处理类
CustomTransactionSynchronization
类实现了
TransactionSynchronization
接口,用于在事务的不同阶段执行特定的逻辑。该接口定义了多个回调方法,每个方法中都加入了日志输出,方便我们了解事务的状态变化。
packagecom.example.demo.listener;importorg.springframework.transaction.support.TransactionSynchronization;/**
* 自定义事务同步处理类,实现 TransactionSynchronization 接口
* 用于在事务的不同阶段执行特定的逻辑。
*/publicclassCustomTransactionSynchronizationimplementsTransactionSynchronization{/**
* 事务挂起时调用的方法。
*/@Overridepublicvoidsuspend(){// 输出事务挂起的日志System.out.println("Transaction suspended");}/**
* 事务恢复时调用的方法。
*/@Overridepublicvoidresume(){// 输出事务恢复的日志System.out.println("Transaction resumed");}/**
* 事务刷新时调用的方法。
*/@Overridepublicvoidflush(){// 输出事务刷新的日志System.out.println("Transaction flushed");}/**
* 事务提交前调用的方法。
* readOnly 参数是一个布尔值,指示当前事务是否为只读事务。true表示当前事务为只读事务,false表示当前事务为读写事务。
* @param readOnly 是否为只读事务
*/@OverridepublicvoidbeforeCommit(boolean readOnly){// 输出事务提交前的日志,显示是否为只读事务System.out.println("Transaction before commit, readOnly: "+ readOnly);}/**
* 事务完成前调用的方法。
*/@OverridepublicvoidbeforeCompletion(){// 输出事务完成前的日志System.out.println("Transaction before completion");}/**
* 事务提交后调用的方法。
*/@OverridepublicvoidafterCommit(){// 输出事务提交后的日志System.out.println("Transaction after commit");}/**
* 事务完成后调用的方法。
*
* @param status 事务完成的状态,STATUS_COMMITTED 或 STATUS_ROLLED_BACK
*/@OverridepublicvoidafterCompletion(int status){// 根据事务完成的状态输出相应的日志if(status ==TransactionSynchronization.STATUS_COMMITTED){// 事务成功提交System.out.println("Transaction completed with status: COMMITTED");}elseif(status ==TransactionSynchronization.STATUS_ROLLED_BACK){// 事务回滚System.out.println("Transaction completed with status: ROLLED_BACK");}}}
Mapper接口
TestMapper
接口定义了插入
Test
对象的方法。
packagecom.example.demo.mapper;importcom.example.demo.model.Test;publicinterfaceTestMapper{// 定义插入Test的方法voidinsertTest(Test test);}
MyBatis Mapper XML文件
MyBatis Mapper XML
文件定义了插入
Test
记录的
SQL
语句。
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPEmapperPUBLIC"-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mappernamespace="com.example.demo.mapper.TestMapper"><!-- 插入记录到test表 --><insertid="insertTest"parameterType="com.example.demo.model.Test">
INSERT INTO test (name) VALUES (#{name})
</insert></mapper>
Model类
packagecom.example.demo.model;// 定义Test类与数据库表test对应publicclassTest{privateLong id;// 主键IDprivateString name;// 名称// 获取IDpublicLonggetId(){return id;}// 设置IDpublicvoidsetId(Long id){this.id = id;}// 获取名称publicStringgetName(){return name;}// 设置名称publicvoidsetName(String name){this.name = name;}}
服务类
TestService
类包含一个事务性的方法
createTest
,该方法在插入记录前后注册自定义的事务同步处理类。
packagecom.example.demo.service;importcom.example.demo.listener.CustomTransactionSynchronization;importcom.example.demo.mapper.TestMapper;importcom.example.demo.model.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importorg.springframework.transaction.support.TransactionSynchronizationManager;@ServicepublicclassTestService{@Autowired// 注入 TestMapperprivateTestMapper testMapper;// 使用自定义的 @MyTransactional 注解@TransactionalpublicvoidcreateTest(Test test){System.out.println("createTest is called.");CustomTransactionSynchronization synchronization =newCustomTransactionSynchronization();TransactionSynchronizationManager.registerSynchronization(synchronization);
testMapper.insertTest(test);// 插入记录// 模拟异常以测试事务回滚if("error".equals(test.getName())){thrownewRuntimeException("Simulated error");}System.out.println("createTest is finished.");}}
主程序类
packagecom.example.demo;importcom.example.demo.configuration.AppConfig;importcom.example.demo.model.Test;importcom.example.demo.service.TestService;importorg.springframework.context.ApplicationContext;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;publicclassDemoApplication{publicstaticvoidmain(String[] args){// 加载 Spring 配置文件ApplicationContext context =newAnnotationConfigApplicationContext(AppConfig.class);// 获取 TestService BeanTestService testService = context.getBean(TestService.class);try{Test test =newTest();
test.setName("success");
testService.createTest(test);System.out.println("Test with name 'success' created.");}catch(Exception e){System.out.println("Failed to create test with name 'success': "+ e.getMessage());}System.out.println("======================");try{Test test =newTest();
test.setName("error");
testService.createTest(test);}catch(Exception e){System.out.println("Failed to create test with name 'error': "+ e.getMessage());}}}
运行结果:
打印日志的配置如下,方便大家运行调试
<configuration><!-- 定义控制台日志输出 --><appendername="console"class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern></encoder></appender><!-- 设置全局日志级别为 INFO --><rootlevel="INFO"><appender-refref="console"/></root><!-- 设置特定包的日志级别 --><loggername="org.springframework.jdbc.datasource.DataSourceTransactionManager"level="INFO"/><loggername="org.mybatis.spring.SqlSessionUtils"level="INFO"/><loggername="com.example.demo.mapper"level="DEBUG"/></configuration>
3. 时序图:通过TransactionSynchronization 接口实现事务监听器
时序图中各个步骤的详细解释:
- 调用业务方法:
- 客户端调用业务服务的方法,开始事务性操作。
- 开启事务:
- 业务服务向事务管理器发起请求,开启一个新的事务。
- 注册同步处理器:
- 事务管理器注册事务监听器,这一步使得监听器能够在事务的不同阶段接收通知和执行特定操作。
- 事务开始:
- 事务监听器记录事务的开始,此时可以进行一些初始化操作。
- 执行业务逻辑:
- 业务服务执行业务逻辑,比如数据库操作等,并返回结果。
- 事务提交前:
- 在事务提交之前,事务管理器调用事务监听器的
beforeCommit(boolean readOnly)
方法。这个方法允许我们在提交之前执行一些操作,如检查事务状态。
- 事务完成前:
- 在事务完成之前,无论是提交还是回滚,事务管理器都会调用事务监听器的
beforeCompletion()
方法。这一步通常用于执行一些清理工作。
- 提交事务或回滚事务:
- 事务管理器决定提交事务还是回滚事务。
- 如果提交事务,事务管理器会先调用事务监听器的
afterCommit()
方法,随后调用afterCompletion(STATUS_COMMITTED)
方法,表示事务成功提交。 - 如果回滚事务,事务管理器直接调用事务监听器的
afterCompletion(STATUS_ROLLED_BACK)
方法,表示事务已经回滚。
- 业务方法完成:
- 客户端收到业务方法执行完成的通知。
- 事务刷新:
- 在某些情况下,事务管理器可能会调用事务监听器的
flush()
方法,强制刷新事务中的某些操作。
- 事务挂起:
- 事务管理器调用事务监听器的
suspend()
方法,事务进入挂起状态,这可能发生在嵌套事务的场景中。
- 事务恢复:
- 事务管理器调用事务监听器的
resume()
方法,恢复挂起的事务。
4. @TransactionalEventListener注解实现事务监听器
除了实现
TransactionSynchronization
接口,我们还可以通过
@TransactionalEventListener
注解来实现事务监听器。这种方式需要事件被发布才能被监听到。
我们在上一小节代码的基础上修改一下。
1.新增自定义事务事件类CustomTransactionEvent
CustomTransactionEvent
类是一个简单的事件类,包含事件类型的字段。
packagecom.example.demo.event;/**
* 自定义事务事件类。
*/publicclassCustomTransactionEvent{privatefinalString eventType;// 事件类型/**
* 构造函数。
*
* @param eventType 事件类型
*/publicCustomTransactionEvent(String eventType){this.eventType = eventType;}/**
* 获取事件类型。
*
* @return 事件类型
*/publicStringgetEventType(){return eventType;}}
2. 新增自定义事务事件监听器TransactionEventListener
TransactionEventListener
类使用
@TransactionalEventListener
注解监听事务事件,并根据事件类型在不同阶段执行操作。
packagecom.example.demo.listener;importcom.example.demo.event.CustomTransactionEvent;importorg.springframework.context.event.EventListener;importorg.springframework.stereotype.Component;importorg.springframework.transaction.event.TransactionPhase;importorg.springframework.transaction.event.TransactionalEventListener;/**
* 自定义事务事件监听器。
*/@ComponentpublicclassTransactionEventListener{/**
* 处理事务提交前事件。
*
* @param event 自定义事务事件
*/@TransactionalEventListener(phase =TransactionPhase.BEFORE_COMMIT)publicvoidbeforeCommit(CustomTransactionEvent event){if("BEFORE_COMMIT".equals(event.getEventType())){System.out.println("Transaction before commit");}}/**
* 处理事务提交后事件。
*
* @param event 自定义事务事件
*/@TransactionalEventListener(phase =TransactionPhase.AFTER_COMMIT)publicvoidafterCommit(CustomTransactionEvent event){if("AFTER_COMMIT".equals(event.getEventType())){System.out.println("Transaction after commit");}}/**
* 处理事务回滚后事件。
*
* @param event 自定义事务事件
*/@TransactionalEventListener(phase =TransactionPhase.AFTER_ROLLBACK)publicvoidafterRollback(CustomTransactionEvent event){System.out.println("Transaction after rollback");}/**
* 处理事务完成后事件。
* 注意:@TransactionalEventListener 注解的 phase 参数实际上是用来控制监听器在事务的哪个阶段触发,而不是决定发布的事件类型。
* @param event 自定义事务事件
*/@TransactionalEventListener(phase =TransactionPhase.AFTER_COMPLETION)publicvoidafterCompletion(CustomTransactionEvent event){if("AFTER_COMPLETION_COMMITTED".equals(event.getEventType())){System.out.println("Transaction completed with status: COMMITTED");}elseif("AFTER_COMPLETION_ROLLED_BACK".equals(event.getEventType())){System.out.println("Transaction completed with status: ROLLED_BACK");}}}
这里特别注意:
@TransactionalEventListener
注解的
phase
参数实际上是用来控制监听器在事务的哪个阶段触发,而不是决定发布的事件类型。如果这里是
eventPublisher.publishEvent(new CustomTransactionEvent("AFTER_COMPLETION_COMMITTED"))
,那么在发布事务完成后的回调方法中,判断该事件类型的时候会打印
Transaction completed with status: COMMITTED
3. 修改TestService服务类
TestService
类修改后,在
createTest
方法中发布自定义事务事件。
packagecom.example.demo.service;importcom.example.demo.event.CustomTransactionEvent;importcom.example.demo.mapper.TestMapper;importcom.example.demo.model.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.ApplicationEventPublisher;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;/**
* Test服务类。
*/@ServicepublicclassTestService{@AutowiredprivateTestMapper testMapper;// 注入TestMapper@AutowiredprivateApplicationEventPublisher eventPublisher;// 注入事件发布器/**
* 创建Test记录。
*
* @param test Test实体
*/@TransactionalpublicvoidcreateTest(Test test){System.out.println("createTest is called.");
testMapper.insertTest(test);// 插入记录// 发布事务提交前事件
eventPublisher.publishEvent(newCustomTransactionEvent("BEFORE_COMMIT"));// 模拟异常以测试事务回滚if("error".equals(test.getName())){thrownewRuntimeException("Simulated error");}// 发布事务提交后事件
eventPublisher.publishEvent(newCustomTransactionEvent("AFTER_COMMIT"));System.out.println("createTest is finished.");}}
再次运行,看看结果:
通过使用
@TransactionalEventListener
注解,我们可以在事务的不同阶段执行相应的逻辑,如记录日志、发送通知、更新缓存等。相比直接实现
TransactionSynchronization
接口,使用注解的方式更加简洁和易于维护。
但是某些回调方法如
suspend()
和
resume()
不能直接通过注解实现,如果事务传播方式比较复杂且有嵌套,还是建议实现
TransactionSynchronization
接口的方式实现事务监听器。
在
Spring
中,通过
@TransactionalEventListener
注解来监听事务事件需要事件被发布才能被监听到。然而当我们使用
TransactionSynchronization
接口时,我们不需要显式地发布事件,而是通过直接实现接口的方法来处理事务的各个阶段。因此,如果希望在事务的各个阶段进行监听,并且不想显式发布事件,使用
TransactionSynchronization
接口是更合适的选择。
5. 时序图:@TransactionalEventListener注解实现事务监听器
时序图具体解释
- 调用业务方法:
- 客户端调用业务服务的方法,开始一段事务性操作。
- 开启事务:
- 业务服务向事务管理器请求开启一个新的事务。事务管理器负责管理事务的生命周期。
- 注册事务事件监听器:
- 事务管理器在事务开启后注册事务事件监听器,使其能够在事务的不同阶段接收事件通知。
- 事务开始:
- 事务事件监听器记录事务的开始,此时可以进行一些初始化操作。
- 执行业务逻辑:
- 业务服务执行实际的业务逻辑,如数据库操作等。完成后返回结果。
- 事务提交前:
- 在事务提交前,业务服务通过事件发布器发布自定义事务事件(
CustomTransactionEvent
),表示事务即将提交。 - 事务事件监听器监听到该事件,并处理该事件(
TransactionPhase.BEFORE_COMMIT
)。
- 提交事务或回滚事务:
- 事务管理器根据业务逻辑的执行结果决定提交事务或回滚事务。
- 提交事务:- 事务提交后:- 提交事务后,业务服务通过事件发布器发布另一个自定义事务事件(
CustomTransactionEvent
),表示事务已成功提交。- 事务事件监听器监听到该事件,并处理该事件(TransactionPhase.AFTER_COMMIT
)。- 事务完成:- 事务提交完成后,再次发布一个事件表示事务已全部完成。- 事务事件监听器处理该事件(TransactionPhase.AFTER_COMPLETION
)。 - 回滚事务:- 事务回滚后: - 如果业务逻辑中出现错误导致事务回滚,业务服务通过事件发布器发布自定义事务事件(
CustomTransactionEvent
),表示事务已回滚。- 事务事件监听器监听到该事件,并处理该事件(TransactionPhase.AFTER_ROLLBACK
)。- 事务完成: - 事务回滚完成后,再次发布一个事件表示事务已全部完成。- 事务事件监听器处理该事件(TransactionPhase.AFTER_COMPLETION
)。
- 业务方法完成:
- 客户端收到业务方法执行完成的通知。
关键点说明
- @TransactionalEventListener 注解:用于监听事务事件,并通过 phase 参数指定监听器在事务的哪个阶段触发。这里展示了
TransactionPhase.BEFORE_COMMIT
、TransactionPhase.AFTER_COMMIT
、TransactionPhase.AFTER_ROLLBACK
和TransactionPhase.AFTER_COMPLETION
四个阶段。 - 事件发布器:在事务的不同阶段,业务服务通过事件发布器发布自定义事务事件(
CustomTransactionEvent
),以通知事务事件监听器。 - 事务事件监听器:监听并处理特定阶段的事务事件,确保在事务的不同阶段执行相应的逻辑。
6. 实际应用场景
- 日志记录:
在事务的不同阶段记录日志,帮助开发人员调试和监控系统的运行状况。例如,在事务提交或回滚时记录日志信息,以追踪事务的执行情况。
- 缓存更新:
在事务提交成功后更新缓存,以确保缓存中的数据与数据库中的数据一致。这样可以避免在事务尚未提交时缓存数据不一致的问题。
- 发送通知:
在事务提交或回滚后发送通知。例如,当订单成功处理时发送确认邮件,当事务回滚时发送警告通知等。
- 数据同步:
在分布式系统中,在事务提交后触发数据同步操作,将数据同步到其他服务或系统中,确保数据的一致性和完整性。
- 审计和合规:
记录事务的详细信息,以满足审计和合规要求。例如,记录每个事务的执行时间、操作用户、操作内容等信息。
- 事件驱动架构:
在事务提交后发布事件,其他组件或服务可以监听这些事件并执行相应的操作。例如,订单服务在订单创建成功后发布订单创建事件,库存服务监听该事件并更新库存。
- 事务补偿:
在事务回滚后执行补偿操作。例如,在分布式事务中,如果一个子事务失败,可以通过事务监听器触发补偿逻辑,撤销已经执行的其他子事务。
- 安全和审计:
在事务完成后记录安全相关信息,如用户行为日志、安全审计日志等,以确保系统的安全性和合规性。
欢迎一键三连~
有问题请留言,大家一起探讨学习
----------------------Talk is cheap, show me the code-----------------------
版权归原作者 砖业洋__ 所有, 如有侵权,请联系我们删除。