news 2026/3/24 14:53:05

事务拦截器TransactionInterceptor

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
事务拦截器TransactionInterceptor

事务拦截器TransactionInterceptor

  • 1. 事务拦截器项目配置
  • 2. 切面
    • 2.1 核心故事:一份油泼面🍽️的旅程
    • 2.2 AOP核心概念📚
    • 2.3 两种代码💻风格实现“餐厅切面”
      • 风格一:基于`@Aspect`注解(现代、声明式、更常用)
      • 风格二:基于`Advisor`编程式配置(底层、灵活、更精确)
      • 核心关系与选择🔄
  • 3. @Transactional解释
    • 两种方式的直观对比🔄
  • 5. 结论与选择💡

1. 事务拦截器项目配置

在Spirng架构中,如果不想使用 @Transactional注解(有时候会忘记,处写这个注解太麻烦),我们就可以使用@Aspect配置式AOP 来装配 事务拦截器(TransactionInterceptor)的方案,完全摆脱@Transactional注解。这是Spring框架内更原生的编程式事务管理方式。

importorg.aspectj.lang.annotation.Aspect;importorg.springframework.aop.Advisor;importorg.springframework.aop.aspectj.AspectJExpressionPointcut;importorg.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;importorg.springframework.aop.support.DefaultPointcutAdvisor;importorg.springframework.aop.support.NameMatchMethodPointcut;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.transaction.PlatformTransactionManager;importorg.springframework.transaction.TransactionDefinition;importorg.springframework.transaction.interceptor.*;importjava.util.Collections;importjava.util.HashMap;importjava.util.Map;@Aspect@ConfigurationpublicclassTransactionConfig{// 1. 定义切入点(拦截 com.pro.service 包下所有方法)privatestaticfinalStringAOP_POINTCUT_EXPRESSION="execution(* com.sh.service..*.*(..))";// 2. 定义增删改方法前缀(自动加事务)privatestaticfinalString[]REQUIRED_RULE_TRANSACTION={"insert*","create*","add*","save*","update*","modify*","del*","delete*","remove*"};// 3. 定义查询方法前缀(自动加只读事务)privatestaticfinalString[]READ_RULE_TRANSACTION={"select*","get*","query*","search*","count*","find*","list*","page*"};// 4. 注入事务管理器@AutowiredprivatePlatformTransactionManagertransactionManager;// ========== 5. 核心:配置事务拦截器 (TransactionInterceptor)【这就是个Adivce(通知)】@BeanpublicTransactionInterceptortxAdvice(){// 5.1 创建事务属性源NameMatchTransactionAttributeSourcetas=newNameMatchTransactionAttributeSource();// 5.2 配置增删改事务属性(REQUIRED)RuleBasedTransactionAttributerequiredTx=newRuleBasedTransactionAttribute();requiredTx.setRollbackRules(Collections.singletonList(newRollbackRuleAttribute(Exception.class)));// 发生异常回滚requiredTx.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);// 事务隔离级别:读已提交requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);// 事务传播行为:REQUIREDrequiredTx.setTimeout(30);// 超时30秒// 5.3 配置查询事务属性(只读)RuleBasedTransactionAttributereadOnlyTx=newRuleBasedTransactionAttribute();readOnlyTx.setRollbackRules(Collections.singletonList(newRollbackRuleAttribute(Exception.class)));readOnlyTx.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_SUPPORTS);// 支持当前事务,不存在也不新建readOnlyTx.setReadOnly(true);// 关键:设置为只读readOnlyTx.setTimeout(20);// 查询超时可设短些// 5.4 将方法名模式映射到事务属性Map<String,TransactionAttribute>txMap=newHashMap<>();for(StringmethodName:REQUIRED_RULE_TRANSACTION){txMap.put(methodName,requiredTx);}for(StringmethodName:READ_RULE_TRANSACTION){txMap.put(methodName,readOnlyTx);}tas.setNameMap(txMap);// 5.5 创建并返回事务拦截器returnnewTransactionInterceptor(transactionManager,tas);}// ========== 6. 核心:配置切面(Advisor),将切入点和拦截器关联@BeanpublicAdvisortxAdviceAdvisor(){// 6.1 创建切入点(使用AspectJ表达式)AspectJExpressionPointcutpointcut=newAspectJExpressionPointcut();pointcut.setExpression(AOP_POINTCUT_EXPRESSION);// 6.2 创建 Advisor(通知器),将切入点 和 事务通知 绑定returnnewDefaultPointcutAdvisor(pointcut,txAdvice());}}

在了解AOP之前先看一张图:


2. 切面

用一个生活化的例子——“餐厅点餐服务”解释AOP的相关概念。

2.1 核心故事:一份油泼面🍽️的旅程

想象你去一家面馆点了一份油泼面。它的核心流程是:
你点餐 → 厨师烹饪 → 你享用。

现在,面馆为了提升体验和管理,在核心流程前后加入了一些“通用服务”:

  1. 点餐前:服务员确认你是VIP会员,以决定是否赠送餐前冰封(饮料)。
  2. 点餐后、烹饪前:系统自动打印订单日志到后厨。
  3. 烹饪后、上桌前:厨师检查面有没有煮熟
  4. 你吃完后:服务员邀请你进行满意度评价

核心思想:煮面是“核心业务逻辑”,而身份验证、日志、检查、评价这些都是“横切关注点”。AOP的作用,就是在不改动“煮面”这段核心代码的情况下,优雅地插入这些通用服务


2.2 AOP核心概念📚

让我们把这个故事映射到AOP的各个概念上:

AOP 概念专业解释面馆通俗理解
连接点 (Join Point)程序执行过程中一个明确的点,如方法调用、异常抛出等。流程中所有可以插入服务的节点。例如:点餐()烹饪()上菜()这些方法 被调用的【时刻】。
切点 (Pointcut)一个表达式,用来匹配和筛选你感兴趣的连接点。选择器。例如:“匹配所有点餐()方法”,或“匹配所有以process开头的方法”。它决定了 “通用服务” 要在哪里生效。
通知 (Advice)切点处执行的动作代码本身。通用服务的具体内容。例如:“验证VIP身份”、“打印日志”这段代码逻辑。
切面 (Aspect)切点 + 通知的完整结合体。它定义了“在何处(切点)执行何种操作(通知)”。一个完整的服务方案。例如:“点餐()方法执行前(切点),执行验证VIP()操作(通知)”,这就是一个完整的切面。
@Aspect一个注解,用来把一个普通的Java类声明为一个切面类。给一个服务方案团队(Java类)挂上牌子,上面写着“我们是提供横切服务的”。
@Before一种通知类型注解,表示该通知在目标方法执行之前运行。前置服务。例如:“在厨师烹饪之前,先打印订单日志”。
AdvisorSpring AOP中更原始、更底层的切面表示。一个Advisor通常只包含一个通知 和 一个切点一个最小化的、不可分割的服务指令。好比一张工单,严格写着:“仅当客户点油泼面时(切点),才在烹饪后做熟度检查(通知)”。它非常精准、直接。

2.3 两种代码💻风格实现“餐厅切面”

现在,我们用两种风格来实现上述的“打印日志”切面(在烹饪方法前打印)。

风格一:基于@Aspect注解(现代、声明式、更常用)

这种方式像用高级语言描述需求。

// 1. 声明这是一个切面类,同时它也是一个Spring管理的Bean@Component@AspectpublicclassRestaurantAspect{// 2. 定义切点:匹配所有在 CookingService 类中的 cook 方法@Pointcut("execution(* com.example.service.CookingService.cook(..))")publicvoidcookMethod(){}// 3. 定义通知,并与切点绑定:在烹饪方法执行前@Before("cookMethod()")publicvoidprintOrderLogBeforeCooking(){System.out.println("[日志] 后厨收到订单,开始烹饪...");// 这里可以拿到请求参数、方法名等信息,实现复杂逻辑}// 其他通知@AfterReturning("cookMethod()")publicvoidcheckSteakDoneness(){System.out.println("[质检] 检查油泼面熟了没...");}}

看完这个类,可以回过头再看一下上面的事务拦截器AOP。

通俗理解:你写了一个服务手册(RestaurantAspect),在里面用标签(@Before)写明:“在做饭这个环节之前,要执行打印日志这个动作”。Spring 看到@Aspect这个牌子后,会自动帮你安排。

风格二:基于Advisor编程式配置(底层、灵活、更精确)

这种方式更像直接编写部署指令。

@ConfigurationpublicclassRestaurantAdvisorConfig{@BeanpublicAdvisorcookingLogAdvisor(){// 1. 创建通知(Advice):定义要做什么MethodBeforeAdvicelogAdvice=newMethodBeforeAdvice(){@Overridepublicvoidbefore(Methodmethod,Object[]args,Objecttarget){System.out.println("[Advisor日志] 准备烹饪食材: "+args[0]);}};// 2. 创建切点(Pointcut):定义在哪里做(精确到方法名匹配)NameMatchMethodPointcutpointcut=newNameMatchMethodPointcut();pointcut.addMethodName("cook");// 只匹配名为 "cook" 的方法// 也可以用 pointcut.setClassFilter() 来限制类// 3. 将通知和切点组装成最小的切面单元:AdvisorreturnnewDefaultPointcutAdvisor(pointcut,logAdvice);}}

通俗理解:你直接发了一张精确的工单Advisor)给系统。工单上明确指令:“听着,只有当一个方法名严格叫cook时(切点),才在它之前执行这个打印动作(通知)。”这个工单自成一体,非常精准。


核心关系与选择🔄

@AspectAdvisor是什么关系?

  • 最终效果一致:在运行时,Spring都会把@Aspect注解的切面转换成一个或多个内部的Advisor去执行。
  • 抽象层级不同@Aspect声明式的,像写配置清单,更符合人类思维,一个类里可以定义很多相关通知。Advisor编程式的API,是Spring AOP的工作基石,更底层、更灵活,一个Advisor通常只做一件事。
  • 为什么两种都存在?@Aspect更简洁易用,涵盖了90%的场景。但当需要极其精细的控制(比如根据复杂条件动态决定是否应用通知),或者与一些旧框架集成时,直接使用Advisor这个底层API会更强大。

我们可以通过@Order确定切面的先后顺序,然后在Spring的调度下完美合作,共同增强业务方法,而业务方法本身对此一无所知——这就是AOP的魅力。


3. @Transactional解释

  • “定义一个Advisor绑定拦截器” 是一种编程式、集中配置的方式
  • 使用 @Transactional 注解,是另一种声明式、分散标注的方式

但两者的终点完全相同:都是为了让 TransactionInterceptor 来拦截方法并管理事务

当你使用 @Transactional 注解时,Spring Boot在启动阶段通过自动配置(TransactionAutoConfiguration)完成了一系列幕后工作,其本质是自动创建了所需的Advisor。在spring中有这样一个源码:

@Configuration(proxyBeanMethods=false)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)@ImportRuntimeHints(TransactionRuntimeHints.class)publicclassProxyTransactionManagementConfigurationextendsAbstractTransactionManagementConfiguration{@Bean(name=TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)publicBeanFactoryTransactionAttributeSourceAdvisortransactionAdvisor(TransactionAttributeSourcetransactionAttributeSource,TransactionInterceptortransactionInterceptor){BeanFactoryTransactionAttributeSourceAdvisoradvisor=newBeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource);advisor.setAdvice(transactionInterceptor);if(this.enableTx!=null){advisor.setOrder(this.enableTx.<Integer>getNumber("order"));}returnadvisor;// 最终还是一个Advisor!}@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)// 创建一个默认的事务拦截器// 这里的入参原本是 TransactionAttributeSource,为了便于理解修改为 AnnotationTransactionAttributeSourcepublicTransactionInterceptortransactionInterceptor(AnnotationTransactionAttributeSourcetransactionAttributeSource){TransactionInterceptorinterceptor=newTransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource);if(this.txManager!=null){interceptor.setTransactionManager(this.txManager);}returninterceptor;}}

整个流程可以概括为:
@Transactional注解 → 被AnnotationTransactionAttributeSource解析为规则 → 与TransactionInterceptor一起被包装进BeanFactoryTransactionAttributeSourceAdvisor→ 该Advisor被应用到所有Bean的创建过程中。

两种方式的直观对比🔄

为了让区别更清晰,我们可以用下面的表格来对比这两种方式:

方面编程式Advisor@Transactional方式(声明式)
配置核心@Configuration类中,显式定义一个AdvisorBean。在方法或类上添加@Transactional注解
规则定义NameMatchTransactionAttributeSource中,通过方法名模式匹配来定义规则。注解的属性中直接定义规则(如@Transactional(readOnly = true))。
切点(Pointcut)显式指定,如execution(* com.pro.service..*.*(..))隐式生成,切点逻辑是“匹配所有有@Transactional注解的地方”。
底层实现直接操控了Spring AOP最底层的AdvisorAdvicePointcut三大件。利用了Spring的自动配置@EnableTransactionManagement,在底层帮我们创建了Advisor
优点集中管理,规则一目了然;与业务代码完全解耦;适合基于方法名的批量规则使用简单,贴近业务逻辑;粒度更细,可以针对单个方法特殊配置;是Spring生态的标准做法
缺点不够灵活,如果某个方法需要例外规则(如REQUIRES_NEW)很难单独配置。注解分散在代码各处;对业务代码有侵入性;无法根据方法名等动态规则批量处理。

5. 结论与选择💡

  • TransactionInterceptor始终在工作:无论你用哪种方式,最终都是TransactionInterceptor这个“事务引擎”在执行拦截和管理。区别只在于“启动引擎的开关”是编程式装配的Advisor,还是由注解触发的自动装配Advisor
  • 可以混合使用(但需谨慎):Spring允许两者共存。如果绝大多数方法遵循统一规则,但极个别方法需要特殊事务行为(比如一个特殊的handlePayment方法需要REQUIRES_NEW),你可以在那个方法上单独添加@Transactional(propagation = Propagation.REQUIRES_NEW)
  • 编程式配置的规则是全局的,而@Transactional注解的规则是局部的,局部注解通常会覆盖全局规则。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/15 20:57:54

深入Spring Boot源码(四):Starter机制与依赖管理深度解析

前言在前面的文章中&#xff0c;我们深入剖析了Spring Boot的自动配置机制。然而&#xff0c;自动配置的实现离不开另一个核心概念——Starter。Starter是Spring Boot生态系统的基石&#xff0c;它将相关的依赖聚合在一起&#xff0c;并与自动配置紧密结合&#xff0c;真正实现…

作者头像 李华
网站建设 2026/3/15 14:58:04

SAPUI5 1.71.78老版本的消费restful服务

为了兼容老浏览器&#xff0c;没用Javascript的现代fetch api&#xff0c;用的jquerycontroller.js代码如下sap.ui.define(["sap/ui/core/mvc/Controller","sap/ui/model/json/JSONModel","sap/m/MessageToast" ], function (Controller, JSONMo…

作者头像 李华
网站建设 2026/3/23 11:07:45

Qwen3-14B-MLX-4bit长文本处理与YaRN扩展

Qwen3-14B-MLX-4bit长文本处理与YaRN扩展 在当前AI模型“军备竞赛”愈演愈烈的背景下&#xff0c;一味追求参数规模已不再是唯一解。越来越多的企业开始意识到&#xff1a;一个能在本地稳定运行、支持复杂任务编排、同时具备超长上下文理解能力的中型模型&#xff0c;往往比“云…

作者头像 李华
网站建设 2026/3/24 9:41:43

php小程序红色文物活动文创产品商城系统APP_2fil7831

文章目录 具体实现截图主要技术与实现手段关于我本系统开发思路java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01; 具体实现截图 同行可拿货,招校园代理 php小程序红色文物活动文创产品商城系统APP_2fil7831 …

作者头像 李华
网站建设 2026/3/15 18:53:34

Excalidraw:手绘风格开源白板工具详解

Excalidraw&#xff1a;当手绘遇上数字白板 你有没有过这样的经历&#xff1f;开会时想快速画个架构图&#xff0c;却卡在工具复杂的菜单里&#xff1b;写技术文档时需要一张示意图&#xff0c;结果花两小时调线条对齐&#xff1b;团队头脑风暴&#xff0c;想法满天飞&#xf…

作者头像 李华
网站建设 2026/3/23 4:50:16

springboot基于微信小程序的员工签到企业项目多人协同办公系统

文章目录具体实现截图主要技术与实现手段关于我本系统开发思路java类核心代码部分展示结论源码lw获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;具体实现截图 同行可拿货,招校园代理 springboot基于微信小程序的员工签到企业项目多人协同办公…

作者头像 李华