• 2.springboot代理调用


    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    动态代理

    本节通过实例查看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 {
    		//**释放资源
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    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);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    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 {
    	    ...
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
  • 相关阅读:
    Editing Conditional Radiance Fields
    JS高阶:深入理解数据、变量、内存
    在生产中部署ML前需要了解的事
    Vue之html中特殊符号的展示
    驾驶证识别易语言代码
    阿里云——云服务器基础运维与管理
    ATA-4051C高压功率放大器在压电电机中的作用是什么
    Nvidia GPU 入门教程之 11 数据中心 GPU 的对决:A100 与 V100S
    消息队列-kafka-服务端处理架构(架构,Topic文件结构,服务端数据的一致性)
    【C++11】列表初始化
  • 原文地址:https://blog.csdn.net/zhouping118/article/details/133383199