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;
    }
}

动态代理

本节通过实例查看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;
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值