2.springboot代理调用

news2024/11/27 17:49:07

1.概述

本文介绍在方法上开启声明式事务@Transactional后(使用InfrastructureAdvisorAutoProxyCreator创建jdk动态代理),springboot的调用该方法的过程;

2.结论(重点)

  • 在方法开启声明式事务后,spring会为该对象创建动态代理。spring容器为该bean创建了JdkDynamicAopProxy实例(jdk动态代理方式)
  • 每一个配置的切面对应一个PointcutAdvisor实例,包含切面和切面需执行的Interceptor(切面的额外逻辑)。所有切面的Interceptor组成拦截器链
  • 调用目标方法时会先调用Interceptor链,最后调用目标方法(原理和servlet过滤器链相同)
  • 在实例的方法A里面直接调用同个实例的另一个方法B,不会经过动态代理的调用。所有在A上没有,但是在B上有的切面都不会生效;

3.过程

测试代码

TestProxyServiceImpl继承TestProxyService接口,test()方法开启了事务(@Transactional)。下面测试/testProx/t1调用TestProxyServiceImpl.test()的过程;

//**接口
public interface TestProxyService {
    String test();
}

//**开启事务的类
@Slf4j
@Service("testProxyService")
public class TestProxyServiceImpl implements TestProxyService {

    //**开启事务的方法
    @Override
    @Transactional
    public String test() {
        String rst = "测试代理被调用,时间:"+ LocalTime.now();
        log.info(rst);

        return rst;
    }
}

//**调用的入口
@Slf4j
@RestController
@RequestMapping("/testProxy")
public class TestProxyController {

    @Autowired
    private TestProxyService testProxyService;
    @Autowired
    private BeanFactory beanFactory;

    @GetMapping(value = "/t1")
    public Object t1(String test) {
        log.info(test);
        this.testProxyService.test();

        return test;
    }
}

动态代理

本节通过实例查看spring创建的jdk动态代理对象,并简单介绍相关的主要接口。后面的章节会详细介绍每个接口的功能和主要实现;
jdk动态代理对象

A.JdkDynamicAopProxy

所有Jdk动态代理对象都是JdkDynamicAopProxy的实例,JdkDynamicAopProxy是Spring封装的jdk动态代理类,实现了InvocationHandler接口的invoke方法。增强逻辑都在JdkDynamicAopProxy的invoke中调用;

B.AdvisedSupport

AdvisedSupport是spring封装的动态代理配置基类,所有方式(包括jdk动态代理和cglib动态代理)的动态代理相关的配置、属性和额外逻辑都在此对象中(比如创建代理对象和调用代理逻辑)。本例实际是使用AdvisedSupport的子类ProxyFactory;

C.TargetSource

TargetSource是对代理前的原对象的封装,提供了getTargetClass方法,获取原对象的class对象。本例把原对象(TestProxyServiceImpl的实例)封装成了SingletonTargetSource;

D.AdvisedSupportListener

AdvisedSupportListener代理对象监听器可以监听代理对象的创建和属性变化,为一个列表。提供了两个方法,activated(代理对象第一次创建时调用)和adviceChanged(代理对象的属性变化时调用)。本例没有配置代理对象监听器;

E.Advisor

Advisor(顾问)是对Advice(通知)的封装,通常内部会持有一个Advice对象。其子接口PointcutAdvisor,添加了一个getPointcut(获取切入点)方法,封装一个切面和一个切面添加的额外逻辑。Advisor是一个列表,支持给一个对象/方法添加多个切面和额外逻辑。本例使用BeanFactoryTransactionAttributeSourceAdvisor,内部持有的切面为TransactionAttributeSourcePointcut,通知为TransactionInterceptor;

F.Advice

Advice(通知)是动态代理对象真正需要额外执行逻辑的封装(此接口才是真正添加的额外逻辑,前面的接口全是封装),常见的有BeforeAdvice(调用之前执行),AfterAdvice(调用之后执行)和Interceptor(调用前后执行)。本例是声明式事务,所以使用了TransactionInterceptor(事务拦截器),在调用前后处理数据库事务开启和提交的逻辑;

调用过程

A.调用开启声明式事务的目标方法

调用testProxyService.test()方法,实际上是调用JdkDynamicAopProxy的invoke方法;

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ......
	try {
		//**处理equals & hashCode 和 AdvisedSupport方法调用

		Object retVal;

		//**获取代理前的原对象
		target = targetSource.getTarget();
		Class<?> targetClass = (target != null ? target.getClass() : null);

		//**获取Interceptor列表
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

		//**如果Interceptor列表为空,直接调用目标方法
		if (chain.isEmpty()) {
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {
			//**调用Interceptor链(依次调用每个Interceptor)
			MethodInvocation invocation =
					new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
			retVal = invocation.proceed();
		}

		//**处理返回值类型
		
		return retVal;
	} finally {
		//**释放资源
	}
}

B.反射调用Interceptor链

使用ReflectiveMethodInvocation.proceed进行反射调用Interceptor链;

  • 每个Interceptor都必须调用ReflectiveMethodInvocation实例的proceed方法,以调用下一个Interceptor;
  • proceed方法每次从Interceptor链取出一个Interceptor执行,直接到所有的Interceptor链被执行完成,最后执行目标方法;
public Object proceed() throws Throwable {
	//**如果Interceptor链全部执行完成,则调用目标方法
	if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
		return invokeJoinpoint();
	}

    //**每次从Interceptor链取出一个Interceptor
	Object interceptorOrInterceptionAdvice =
			this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
	//**如果拦截器为InterceptorAndDynamicMethodMatcher,则根据规则进行匹配。匹配上了就调用对应的Interceptor,匹配不上就直接调用下一个Interceptor
	if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher dm) {
		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 {
			return proceed();
		}
	}
	else {
		//**调用Interceptor
		return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
	}
}

C.事务处理&调用目标方法

使用TransactionInterceptor.invoke进行事务处理,在目标方法test()执行前开启事务,然后执行目标方法,最后提交事务;

  • spring新增加了ReactiveTransactionManager(响应式事务管理器,非阻塞事务管理),没有具体实现;
  • spring新增加了CallbackPreferringPlatformTransactionManager(允许传入TransactionCallback接口实例处理业务逻辑),没有具体实现;
public Object invoke(MethodInvocation invocation) throws Throwable {
    //**获取目标class对象(代理前的原calss对象)
	Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

	//**调用父类的方法(在事务中执行下一个Interceptor和目标方法)
	return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
		@Override
		@Nullable
		public Object proceedWithInvocation() throws Throwable {
		    //**调用Interceptor链中的下一个Interceptor
			return invocation.proceed();
		}
		@Override
		public Object getTarget() {
			return invocation.getThis();
		}
		@Override
		public Object[] getArguments() {
			return invocation.getArguments();
		}
	});
}

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

	//**获取事务管理器
	TransactionAttributeSource tas = getTransactionAttributeSource();
	final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
	final TransactionManager tm = determineTransactionManager(txAttr);

    //**处理ReactiveTransactionManager(响应式事务管理器,非阻塞事务管理,没有具体实现)的逻辑
	if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {
		...
	}

	PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
	final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
    //**标准事务管理
	if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
		TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

		Object retVal;
		try {
			//**在事务中执行下一个Interceptor和目标方法
			retVal = invocation.proceedWithInvocation();
		}
		catch (Throwable ex) {
		    //**发生异步后,如果配置了异常回滚则回滚事务,否则提交事务
			completeTransactionAfterThrowing(txInfo, ex);
			throw ex;
		}
		finally {//**清除事务信息
			cleanupTransactionInfo(txInfo);
		}

        //**依赖vavr包的操作,vavr当前最新版本为2021年的0.10.4,很久没更新了(不知道是不是不更新了,暂不深究)
		if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
			TransactionStatus status = txInfo.getTransactionStatus();
			if (status != null && txAttr != null) {
				retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
			}
		}
        
        //**提交事务
		commitTransactionAfterReturning(txInfo);
		return retVal;
	}
    //**处理CallbackPreferringPlatformTransactionManager(允许传入TransactionCallback接口实例处理业务逻辑,没有具体实现)的逻辑
	else {
	    ...
	}
}

4.存在问题

在TestProxyService里面的test2()里面调用test(),不会经过JdkDynamicAopProxy的invoke调用,而是直接调用test()。所以test()的事务不会生效;
结论:在实例的方法A里面直接调用同个实例的另一个方法B,不会经过动态代理的调用。所有在A上没有,但是在B上有的切面都不会生效;

public class TestProxyServiceImpl implements TestProxyService {

    @Override
    @Transactional
    public String test() {
        String rst = "测试代理被调用,时间:"+ LocalTime.now();
        log.info(rst);

        return rst;
    }

    @Override
    public String test2() {
        String rst = "测试代理被调用2,时间:"+ LocalTime.now();
        log.info(rst);

        //**调用开启声明式事务的方法,不会开启事务
        this.test();

        return rst;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1052796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Jetpack组件架构:ViewModel的原理

Android Jetpack组件架构&#xff1a;ViewModel的原理 导言 本篇文章是关于介绍ViewModel的&#xff0c;由于ViewModel的使用还是挺简单的&#xff0c;这里就不再介绍其的基本应用&#xff0c;我们主要来分析ViewModel的原理。 ViewModel的生命周期 众所周知&#xff0c;一般…

聚观早报 | 2024款小鹏P5全新发布;华为发布13.2英寸MatePad Pro

【聚观365】9月26日消息 2024款小鹏P5全新发布 华为发布13.2英寸MatePad Pro 特斯拉发布人形机器人最新进展 百川智能发布Baichuan2-53B 软件行业仍将人才供不应求 2024款小鹏P5全新发布 继2024款小鹏G9问世仅一周&#xff0c;小鹏汽车再度发力新产品&#xff0c;2024款小…

【小沐学前端】Node.js实现UDP通信

文章目录 1、简介2、下载和安装3、代码示例3.1 HTTP3.2 UDP单播3.4 UDP广播 结语 1、简介 Node.js 是一个开源的、跨平台的 JavaScript 运行时环境。 Node.js 是一个开源和跨平台的 JavaScript 运行时环境。 它是几乎任何类型项目的流行工具&#xff01; Node.js 在浏览器之外…

2.4g无线收发芯片:Ci24R1(DFN8)

Ci24R1 采用GFSK/FSK数字调制与解调技术。数据传输速率与PA输出功率都可以调节&#xff0c;支持2Mbps, 1Mbps, 250Kbps三种数据速率。高的数据速率可以在更短的时间完成同样的数据收发&#xff0c;因此可以具有更低的功耗。 Ci24R1 是一颗工作在2.4GHz ISM频段&#xff0c;专为…

医疗实施-住院流程详解

住院就诊流程详解 1.病人入院登记2.病人进入病区3.医生操作病人4.医嘱录入与审核执行5. 医嘱收费前在对应业务系统的操作5.1.药物医嘱5.2.检查检验医嘱5.3.手术医嘱 6.住院医嘱费用的产生7. 医嘱收费后在对应业务系统的操作8. 病人出院 这篇文章是基于我的文章《医疗实施-住院就…

8.3Jmeter使用json提取器提取数组值并循环(循环控制器)遍历使用

Jmeter使用json提取器提取数组值并循环遍历使用 响应返回值例如&#xff1a; {"code":0,"data":{"totalCount":11,"pageSize":100,"totalPage":1,"currPage":1,"list":[{"structuredId":&q…

[React] 性能优化相关

文章目录 1.React.memo2.useMemo3.useCallback4.useTransition5.useDeferredValue 1.React.memo 当父组件被重新渲染的时候&#xff0c;也会触发子组件的重新渲染&#xff0c;这样就多出了无意义的性能开销。如果子组件的状态没有发生变化&#xff0c;则子组件是不需要被重新渲…

百度网盘的扩容

百度网盘的扩容怎么扩 百度网盘的扩容通常需要购买额外的存储空间。以下是扩容百度网盘存储空间的一般步骤&#xff1a; 登录百度网盘&#xff1a;首先&#xff0c;在您的计算机或移动设备上打开百度网盘&#xff0c;并使用您的百度账号登录。 选择扩容选项&#xff1a;一旦登…

数据结构题型12-链式队列

#include <iostream> //引入头文件 using namespace std;typedef int Elemtype;#define Maxsize 5 #define ERROR 0 #define OK 1typedef struct LinkNode {Elemtype data;struct LinkNode* next; }LinkNode;typedef struct {LinkNode* front;LinkNode* rear; }LinkQ…

java项目之小说阅读网站(ssm源码+文档)

项目简介 小说阅读网站实现了以下功能&#xff1a; 管理员&#xff1a;首页、个人中心、读者管理、作者管理、小说信息管理、小说分类管理、余额充值管理、购买小说管理、下载小说管理、系统管理。读者&#xff1a;个人中心、余额充值管理、购买小说管理、下载小说管理、我的…

黑豹程序员-架构师学习路线图-百科:Java

1、简介 Java是Sun公司推出的一门面向对象的编程语言&#xff0c;它是一种通过解释方式来执行的语言。 它出身名门&#xff0c;简化C而来&#xff0c;但并未照搬。继承了C语言各种优点&#xff0c;却摒弃了C里的多继承、指针等概念&#xff0c;因此Java语言具有功能强大和简单…

编码规范、git 规范

1、eslint配置&#xff0c;让代码变得更加规范&#xff0c;就是定义一些规则&#xff0c;开发人员要去遵守 该文件也是推荐在根目录下使用 2、prettier 就是格式化开发人员的代码 1、vscode 安装 prettier 插件 在项目根目录下创建一个 .prettierrc文件 然后去prettier 官网…

数据可视化项目管理软件推荐:提升团队效率的利器

项目管理是一个复杂的学科&#xff0c;实际工作中&#xff0c;即使项目计划做得再好&#xff0c;项目工作中也难免会出现一些意料之外的情况。 可视化程度低就是项目风险不易控、项目问题频发的一个原因。而数据可视化项目管理软件可以解决这些问题&#xff0c;帮助项目团队管控…

uniapp快速入门系列(1)- 概述与基础知识

章节三&#xff1a;抖音小程序页面开发 第1章&#xff1a;概述与基础知识1.1 uniapp简介1.1.1 什么是uniapp&#xff1f;1.1.2 为什么选择uniapp&#xff1f;1.1.3 uniapp与微信小程序的关系 1.2 HBuilderX介绍与安装1.2.1 什么是HBuilderX&#xff1f;1.2.2 HBuilderX的安装1.…

第1篇 目标检测概述 —(4)目标检测评价指标

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。目标检测评价指标是用来衡量目标检测算法性能的指标&#xff0c;可以分为两类&#xff0c;包括框级别评价指标和像素级别评价指标。本节课就给大家重点介绍下目标检测中的相关评价指标及其含义&#xff0c;希望大家学习之后…

如何把word的页眉页脚改为图片

前言 亲戚A&#xff1a; 听说你是计算机专业&#xff1f; 沐风晓月&#xff1a; 是啊 亲戚A&#xff1a; 那正好&#xff0c;来看看我这个页眉怎么改成图片 沐风晓月&#xff1a; 一万匹马奔腾而过 亲戚B&#xff1a; 听说你是英语专业&#xff1f; 沐风晓月&#xff1a; 是啊…

迭代器,可迭代对象,生成器

目录 结论&#xff1a; 1&#xff1a;可迭代对象&#xff1a; 2&#xff1a;生成器&#xff1a;概念如下&#xff1a; 3&#xff1a;迭代器的定义&#xff1a;要同时满足以下三点 一&#xff1a;可迭代对象的分类 二&#xff1a;迭代器的意义和应用场景 1&#xff1a;迭代…

第一届“龙信杯”电子数据取证竞赛Writeup

目录 移动终端取证 请分析涉案手机的设备标识是_______。&#xff08;标准格式&#xff1a;12345678&#xff09; 请确认嫌疑人首次安装目标APP的安装时间是______。&#xff08;标准格式&#xff1a;2023-09-13.11:32:23&#xff09; 此检材共连接过______个WiFi。&#x…

STM32 DMA从存储器发送数据到串口

1.任务描述 &#xff08;1&#xff09;ds18b20测量环境温度存储到存储器&#xff08;数组&#xff09;中。 &#xff08;2&#xff09;开启DMA将数组中的内容&#xff0c;通过DMA发送到串口 存在问题&#xff0c;ds18b20读到的数据是正常的&#xff0c;但是串口只是发送其低…

面试打底稿⑥ 项目一的第二部分

简历原文 抽查部分 计算运费模块板块扩展性优化&#xff0c;采用责任链模式&#xff0c;实现不同地区间寄件的运费模板扩展的优化&#xff0c;为模块解耦&#xff0c;提高了系统的扩展性 短信模块设计&#xff0c;设计了短信发送数据模板的数据化存储&#xff0c;规范了发送短…