当前位置: 首页 > news >正文

spring tx @Transactional 详解 `Advisor`、`Target`、`ProxyFactory

在Spring中,@Transactional注解的处理涉及到多个关键组件,包括AdvisorTargetProxyFactory等。下面是详细的解析和代码示例,解释这些组件是如何协同工作的。

1. 关键组件介绍

1.1 Advisor

Advisor是一个Spring AOP的概念,它包含了切点(Pointcut)和通知(Advice)。在事务管理中,TransactionAttributeSourceAdvisor是一个典型的Advisor。

1.2 Target

Target是指被代理的目标对象,即实际执行业务逻辑的对象。

1.3 ProxyFactory

ProxyFactory是Spring提供的用于创建代理对象的工厂类。它可以使用JDK动态代理或CGLIB创建代理对象。

2. @Transactional的处理流程

  1. 解析注解:Spring扫描@Transactional注解。
  2. 创建Advisor:创建包含事务处理逻辑的Advisor
  3. 创建代理对象:使用ProxyFactory为目标对象创建代理对象,并将Advisor加入到代理对象中。

3. 代码示例

3.1 配置类

首先,通过@EnableTransactionManagement启用事务管理。

import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;@Configuration
@EnableTransactionManagement
public class AppConfig {// DataSource, EntityManagerFactory, TransactionManager beans configuration
}
3.2 目标对象和接口

定义一个业务接口和其实现类:

public interface MyService {void myTransactionalMethod();
}@Service
public class MyServiceImpl implements MyService {@Override@Transactionalpublic void myTransactionalMethod() {// 业务逻辑System.out.println("Executing myTransactionalMethod");}
}
3.3 ProxyFactory和Advisor

使用ProxyFactoryTransactionAttributeSourceAdvisor来创建代理对象并处理事务:

import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.transaction.interceptor.TransactionAttributeSource;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;public class ProxyFactoryExample {public static void main(String[] args) {// 创建目标对象MyService target = new MyServiceImpl();// 创建事务属性源TransactionAttributeSource transactionAttributeSource = new NameMatchTransactionAttributeSource();// 创建事务拦截器TransactionInterceptor transactionInterceptor = new TransactionInterceptor();transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);// 创建AdvisorDefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();advisor.setAdvice(transactionInterceptor);// 创建ProxyFactoryProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAdvisor(advisor);// 创建代理对象MyService proxy = (MyService) proxyFactory.getProxy();// 调用代理对象的方法proxy.myTransactionalMethod();}
}

4. 详细解释

4.1 创建目标对象
MyService target = new MyServiceImpl();

这是被代理的目标对象,它包含了业务逻辑,并使用了@Transactional注解。

4.2 创建事务属性源
TransactionAttributeSource transactionAttributeSource = new NameMatchTransactionAttributeSource();

TransactionAttributeSource用于解析事务属性。NameMatchTransactionAttributeSource是一个实现类,可以基于方法名称匹配事务属性。

4.3 创建事务拦截器
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);

TransactionInterceptor实现了MethodInterceptor接口,用于在方法调用前后处理事务逻辑。

4.4 创建Advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setAdvice(transactionInterceptor);

DefaultPointcutAdvisor包含了事务拦截器,可以在匹配的方法上应用事务逻辑。

4.5 创建ProxyFactory
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvisor(advisor);

ProxyFactory用于创建代理对象。它将目标对象和Advisor结合起来,生成代理对象。

4.6 创建代理对象并调用方法
MyService proxy = (MyService) proxyFactory.getProxy();
proxy.myTransactionalMethod();

通过ProxyFactory.getProxy()方法创建代理对象,并调用代理对象的方法。这时,事务拦截器会在方法调用前后执行事务处理逻辑。

5. 总结

通过以上代码示例,可以看出Spring如何解析@Transactional注解,并使用AdvisorTargetProxyFactory创建代理对象来处理事务逻辑。这些组件协同工作,实现了自动的事务管理。

proxyFactory源码

targetSource与代理对象是两个对象,
在调用代理对象的时候,实际上是要被DynamicAdvisedInterceptor拦截,之后在拦截方法中执行执行拦截器调用链,并把targetSource传给拦截器。
相当于把targetSource对象(非类)作为成员变量传递给代理对象,然后对targetSource对象的方法调用增强。
类->BeanDefinition->bean初始化->为bean添加后置处理器,替换bean对象为proxy对象,其中bean对象作为代理对象的成员变量targetSource,代理对象通过在拦截方法中对targetSource对象的方法调用前后执行advisor的方法。

对比 @Configuration对@bean方法的处理,是直接生成子类,子类重写@bean方法,子类作为BeanDefinition替换原始@Configuration注解的类, 因此在子类中的this调用就是对子类重写的方法调用。

//CglibAopProxy
/*** General purpose AOP callback. Used when the target is dynamic or when the* proxy is not frozen.*/private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {private final AdvisedSupport advised;public DynamicAdvisedInterceptor(AdvisedSupport advised) {this.advised = advised;}@Override@Nullablepublic Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {Object oldProxy = null;boolean setProxyContext = false;Object target = null;TargetSource targetSource = this.advised.getTargetSource();try {if (this.advised.exposeProxy) {// Make invocation available if necessary.oldProxy = AopContext.setCurrentProxy(proxy);setProxyContext = true;}// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...target = targetSource.getTarget();Class<?> targetClass = (target != null ? target.getClass() : null);List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);Object retVal;// Check whether we only have one InvokerInterceptor: that is,// no real advice, but just reflective invocation of the target.if (chain.isEmpty() && CglibMethodInvocation.isMethodProxyCompatible(method)) {// We can skip creating a MethodInvocation: just invoke the target directly.// Note that the final invoker must be an InvokerInterceptor, so we know// it does nothing but a reflective operation on the target, and no hot// swapping or fancy proxying.Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);retVal = invokeMethod(target, method, argsToUse, methodProxy);}else {// We need to create a method invocation...// 链式调用retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();}retVal = processReturnType(proxy, target, method, retVal);return retVal;}finally {if (target != null && !targetSource.isStatic()) {targetSource.releaseTarget(target);}if (setProxyContext) {// Restore old proxy.AopContext.setCurrentProxy(oldProxy);}}}@Overridepublic boolean equals(@Nullable Object other) {return (this == other ||(other instanceof DynamicAdvisedInterceptor &&this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));}/*** CGLIB uses this to drive proxy creation.*/@Overridepublic int hashCode() {return this.advised.hashCode();}}
//ReflectiveMethodInvocation
@Override@Nullablepublic Object proceed() throws Throwable {// We start with an index of -1 and increment early.if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {return invokeJoinpoint();}Object interceptorOrInterceptionAdvice =this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {// Evaluate dynamic method matcher here: static part will already have// been evaluated and found to match.InterceptorAndDynamicMethodMatcher dm =(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {return dm.interceptor.invoke(this);}else {// Dynamic matching failed.// Skip this interceptor and invoke the next in the chain.return proceed();}}else {// It's an interceptor, so we just invoke it: The pointcut will have// been evaluated statically before this object was constructed.//调用 @Transactional事务拦截器return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);}}
//TransactionInterceptor@Nullablepublic Object invoke(MethodInvocation invocation) throws Throwable {Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;return this.invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {@Nullablepublic Object proceedWithInvocation() throws Throwable {// 拦截器链式调用return invocation.proceed();}public Object getTarget() {return invocation.getThis();}public Object[] getArguments() {return invocation.getArguments();}});}

拦截器链式调用

相关文章:

spring tx @Transactional 详解 `Advisor`、`Target`、`ProxyFactory

在Spring中&#xff0c;Transactional注解的处理涉及到多个关键组件&#xff0c;包括Advisor、Target、ProxyFactory等。下面是详细的解析和代码示例&#xff0c;解释这些组件是如何协同工作的。 1. 关键组件介绍 1.1 Advisor Advisor是一个Spring AOP的概念&#xff0c;它包…...

`CyclicBarrier` 是 Java 中的一个同步辅助工具类,它允许一组线程相互等待,直到所有线程都达到了某个公共屏障点(barrier point)

CyclicBarrier 是 Java 中的一个同步辅助工具类&#xff0c;它允许一组线程相互等待&#xff0c;直到所有线程都达到了某个公共屏障点&#xff08;barrier point&#xff09;。当所有线程都到达屏障点时&#xff0c;它们可以继续执行后续操作。CyclicBarrier 的特点是可以重复使…...

华为机试HJ108求最小公倍数

华为机试HJ108求最小公倍数 题目&#xff1a; 想法&#xff1a; 要找到输入的两个数的最小公倍数&#xff0c;这个最小公倍数要大于等于其中最大的那个数值&#xff0c;遍历最大的那个数值的倍数&#xff0c;最大的最小公倍数就是输入的两个数值的乘积 input_number_list i…...

Debezium报错处理系列之第114篇:No TableMapEventData has been found for table id:256.

Debezium报错处理系列之第114篇:Caused by: com.github.shyiko.mysql.binlog.event.deserialization.MissingTableMapEventException: No TableMapEventData has been found for table id:256. Usually that means that you have started reading binary log within the logic…...

开发者必看:MySQL主从复制与Laravel读写分离的完美搭配

介绍 主从同步配置的主要性不用多说&#xff0c;本文将详细介绍了如何在MySQL数据库中设置主从复制&#xff0c;以及如何在Laravel框架中实现数据库的读写分离。 通过一系列的步骤&#xff0c;包括修改MySQL配置、创建同步账户、获取二进制日志文件名和位置、导出主服务器数据…...

二战架构师,拿下

前言 已经许久更新文章了&#xff0c;并不是因为我懒了&#xff0c;而是在备考系统架构师考试。个人感觉还是比较幸运的&#xff0c;低分飘过。现阶段任务也算完成了&#xff0c;记录一下感受。 什么是软考 软考&#xff0c;全称“计算机技术与软件专业技术资格&#xff08…...

泛微开发修炼之旅--35关于基于页面扩展和自定义按钮实现与后端交互调用的方法

文章链接&#xff1a;35关于基于页面扩展和自定义按钮实现与后端交互调用的方法...

原创作品—数据可视化大屏

设计数据可视化大屏时&#xff0c;用户体验方面需注重以下几点&#xff1a;首先&#xff0c;确保大屏信息层次分明&#xff0c;主要数据突出显示&#xff0c;次要信息适当弱化&#xff0c;帮助用户快速捕捉关键信息。其次&#xff0c;设计应直观易懂&#xff0c;避免复杂难懂的…...

AdaBoost集成学习算法理论解读以及公式为什么这么设计?

本文致力于阐述AdaBoost基本步骤涉及的每一个公式和公式为什么这么设计。 AdaBoost集成学习算法基本上遵从Boosting集成学习思想&#xff0c;通过不断迭代更新训练样本集的样本权重分布获得一组性能互补的弱学习器&#xff0c;然后通过加权投票等方式将这些弱学习器集成起来得到…...

uniapp内置组件uni.navigateTo跳转后页面空白问题解决

文章目录 导文空白问题 导文 在h5上跳转正常 但是在小程序里面跳转有问题 无任何报错 页面跳转地址显示正确&#xff0c;但页面内容为空 空白问题 控制台&#xff1a; 问题解决&#xff1a; 方法1&#xff1a; 可能是没有注册的问题&#xff0c;把没注册的页面 注册一下。 方…...

使用树莓派进行python开发,控制电机的参考资料

网站连接&#xff1a;https://www.cnblogs.com/kevenduan?page1 1、简洁的过程步骤&#xff0c; 2、有代码示例&#xff0c; 3、有注意事项&#xff0c;...

protobuf的使用

protobuf&#xff1a;是一种数据格式&#xff0c;独立于平台&#xff0c;独立于语言&#xff0c;是一种二进制格式&#xff0c;可以存储更加复杂的数据结构&#xff0c;比如图&#xff0c;树&#xff0c;结构体&#xff0c;类 作用&#xff1a; 1.持久化&#xff1a;把数据存…...

笔记15:while语句编程练习

练习一&#xff1a; 编写程序&#xff0c;求 2^24^26^2...n^2? -直到累加和大于或等于 10000 为止&#xff0c;输出累加和 -输出累加式中的项数&#xff0c;以及最大的数 n #include<stdio.h> int main() {int sum 0;int i 1;int n 0;while(sum < 10000)//将sum…...

打开excel时弹出stdole32.tlb

问题描述 打开excel时弹出stdole32.tlb 如下图&#xff1a; 解决方法 打开 Microsoft Excel 并收到关于 stdole32.tlb 的错误提示时&#xff0c;通常意味着与 Excel 相关的某个组件或类型库可能已损坏或不兼容。 stdole32.tlb 是一个用于存储自动化对象定义的类型库&#x…...

349. 两个数组的交集

哈喽&#xff01;大家好&#xff0c;我是奇哥&#xff0c;一位专门给面试官添堵的职业面试员 文章持续更新&#xff0c;可以微信搜索【小奇JAVA面试】第一时间阅读&#xff0c;回复【资料】更有我为大家准备的福利哟&#xff01; 文章目录 一、题目二、答案三、总结 一、题目 …...

重庆交通大学数学与统计学院携手泰迪智能科技共建的“智能工作室”

2024年7月4日&#xff0c;重庆交通大学数学与统计学院与广东泰迪智能科技股份有限公司携手共建的“智能工作室”授牌仪式在南岸校区阳光会议室举行。此举标志着数统学院与广东泰迪公司校企合作新篇章的开启&#xff0c;也预示着学院在智能科技教育领域的深入探索和实践。 广东…...

Pandas在生物信息学中的应用详解

Pandas在生物信息学中的应用详解 引言 生物信息学作为一门将计算机科学和生物学相结合的跨学科领域&#xff0c;正随着高通量实验技术的飞速发展而日益重要。Pandas&#xff0c;作为Python中一个强大的数据处理库&#xff0c;为生物信息学研究提供了便捷高效的数据处理和分析…...

ByteMD富文本编辑器的vue3配置

Git地址&#xff1a;GitHub - bytedance/bytemd: ByteMD v1 repository 控制面板输入 npm install bytemd/vue-next 下载成功后在src/main.ts中引用 import "bytemd/dist/index.css";引入后保存&#xff0c;下面是一些插件&#xff0c;比如说我用到gmf和hightLight&…...

基于antdesign封装一个react的上传组件

项目中遇到了一个上传的需求&#xff0c;看了一下已有的代码很粗糙&#xff0c;而且是直接引用andt的组件&#xff0c;体验不太好&#xff0c;自己使用FormData对象封装了一个上传组件&#xff0c;仅供参考。 代码如下&#xff1a; /*** FileUploadModal* description - 文件选…...

ARM裸机:一步步点亮LED(汇编)

硬件工作原理及原理图查阅 LED物理特性介绍 LED本身有2个接线点&#xff0c;一个是LED的正极&#xff0c;一个是LED的负极。LED这个硬件的功能就是点亮或者不亮&#xff0c;物理上想要点亮一颗LED只需要给他的正负极上加正电压即可&#xff0c;要熄灭一颗LED只需要去掉电压即可…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径

目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...