spring中expose-proxy的作用与原理

先看一下几个问题,下面是段代码:

public interface UserService{
	public void a();
	public void a();
}

public class UserServiceImpl implements UserService{
	@Transactional(propagation = Propagation.REQUIRED)
	public void a(){
		this.b();
	}
	@Transactional(propagation = Propagation.REQUIRED_NEW)
	public void b(){
		System.out.println("b has been called");
	}
}

ps:代码参照《Spring源码解析》p173页。
Q1:b中的事务会不会生效?
A1:不会,a的事务会生效,b中不会有事务,因为a中调用b属于内部调用,没有通过代理,所以不会有事务产生。
Q2:如果想要b中有事务存在,要如何做?
A2: ,设置expose-proxy属性为true,将代理暴露出来,使用AopContext.currentProxy()获取当前代理,将this.b()改为((UserService)AopContext.currentProxy()).b()

下面看下expose-proxy在spring中的作用:

在AopNamespaceUtils中的方法

public static void registerAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {
	BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
				parserContext.getRegistry(), parserContext.extractSource(sourceElement));
		//处理proxy-target-class与expose-proxy属性
		useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
		registerComponentIfNecessary(beanDefinition, parserContext);
	}

spring中expose-proxy的作用与原理_第1张图片
spring中expose-proxy的作用与原理_第2张图片
跟踪代码至此,发现蓝色部分代码这里将该参数设置到了BeanDefinition中,那么这个参数在哪里使用的呢。跳过aop生成代理的过程,直接看代理的调用的地方,以JdkDynamicAopProxy为例,cglib方式的地方与jdk一致。对JDK方式的动态代理,都是通过invoke方法执行目标对象的方法,那么来看一下spring中JdkDynamicAopProxy的invoke方法源码。

/**
	 * Implementation of {@code InvocationHandler.invoke}.
	 * 

Callers will see exactly the exception thrown by the target, * unless a hook method throws an exception. */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class targetClass = null; Object target = null; try { if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we "own" the target, // in case it comes from a pool. target = targetSource.getTarget(); if (target != null) { targetClass = target.getClass(); } // Get the interception chain for this method. List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // 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. retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args); } else { // We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); } // Massage return value if necessary. Class returnType = method.getReturnType(); if (retVal != null && retVal == target && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) { // Special case: it returned "this" and the return type of the method // is type-compatible. Note that we can't help if the target sets // a reference to itself in another returned object. retVal = proxy; } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) { throw new AopInvocationException( "Null return value from advice does not match primitive return type for: " + method); } return retVal; } finally { if (target != null && !targetSource.isStatic()) { // Must have come from TargetSource. targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }

spring中expose-proxy的作用与原理_第3张图片
spring中expose-proxy的作用与原理_第4张图片
主要关注这两段代码,发现如果expose-proxy属性值为true的话会调用
oldProxy = AopContext.setCurrentProxy(proxy);
那么看一下这个方法
spring中expose-proxy的作用与原理_第5张图片
spring中expose-proxy的作用与原理_第6张图片
通过英文注释应该就大概明白了,这里是通过一个ThreadLocal来保存代理的,在每次调用代理的时候会判断一下exposeProxy是否为true,如果是的话,就通过ThreadLocal保存代理,可通过以下方法获取到代理。
spring中expose-proxy的作用与原理_第7张图片
然后invoke方法的finally块里又把oldProxy放回了ThreadLocal中,这里要注意一下,就是每次调用都会set一下当前的代理,最后再把之前保存的放回去。

那么之前在问题中,a中调用b的时候通过从ThreadLocal中获取到当前代理再执行b方法就可以达到b中也有事务的要求了。简单来说,处理的方法就是设置expose-proxy属性为true暴露代理。

你可能感兴趣的:(spring)