Spring事务增强

一:问题描述

1.事务配置

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
 <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
            <tx:method name="*" propagation="SUPPORTED" read-only="true"/>
        </tx:attributes>
    </tx:advice>
     <aop:config>
        <aop:pointcut id="transactionPointcut" expression="execution(* com.ph3636.*.service.*.*(..))"/>
        <aop:advisor pointcut-ref="transactionPointcut" advice-ref="transactionAdvice"/>
    </aop:config>
2.示例代码

包路径com.ph3636.test.service.Test1Service
@Service
public class Test1Service{
 @Autowired
    private Test2Service test2Service;
     public void test(){
            test2Service.add();①
        add();
    }
    public void add(){
        test2Service.add();②
        int i = 1 / 0;//这里可以替换为一个更隐秘的获取数据库连接失败错误
        test3Service.add();③
    }
}

3.执行test方法后会成功执行①②,令人匪夷所思的是②为什么没有跟随这个错误一块回滚,而是自己单独提交成功?

二:事务解析

1.xml文件里面的那些方法正则表达式(add*)配置会随着Spring的IOC容器启动并且在Bean进行初始化的时候放出对应的类NameMatchTransactionAttributeSource的属性Map<String, TransactionAttribute> nameMap中,每个配置的方法的名字,隔离级别,事务传播属性都在TransactionAttribute中。

2.事务的aop处理会读取切点配置的类路径expression="execution(* com.ph3636.*.service.*.*(..))",以及需要的通知transactionAdvice也就是上面的方法处理,然后把这些处理组成一个事务拦截器TransactionInterceptor,最后形成一个代理。

3.因为这些Service类没有具体的实现接口,所以spring默认会用CglibAopProxy生成对应的代理类,然后等到执行目标方法时会经过回调Callback进而调用intercept方法开始执行具体的操作

4.创建代理的入口为bean初始化时遇到FactoryBean这个类型的类时会调用他ProxyFactoryBean的getObject方法

private synchronized Object getSingletonInstance() {
		if (this.singletonInstance == null) {
			this.targetSource = freshTargetSource();
			if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
				// Rely on AOP infrastructure to tell us what interfaces to proxy.
				Class<?> targetClass = getTargetClass();
				if (targetClass == null) {
					throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
				}
				setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
			}
			// Initialize the shared singleton instance.
			super.setFrozen(this.freezeProxy);
			this.singletonInstance = getProxy(createAopProxy());
		}
		return this.singletonInstance;
	}

然后用默认的DefaultAopProxyFactory代理工厂创建代理类

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface()) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}
这里会判断适用那种方法创建代理类,Cglib还是JDK自带的,最后返回AopProxy。
5.根据这个aop代理CglibAopProxy创建具体的代理对象getProxy(createAopProxy());

public Object getProxy(ClassLoader classLoader) {
....
Callback[] callbacks = getCallbacks(rootClass);
...
return createProxyClassAndInstance(enhancer, callbacks);
}
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
		// Choose an "aop" interceptor (used for AOP calls).
		Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);主要是这个
.....
		Callback[] mainCallbacks = new Callback[]{
			aopInterceptor, // for normal advice
			targetInterceptor, // invoke target without considering advice, if optimized
			new SerializableNoOp(), // no override for methods mapped to this
			targetDispatcher, this.advisedDispatcher,
			new EqualsInterceptor(this.advised),
			new HashCodeInterceptor(this.advised)
		};
                  ......
		Callback[] callbacks;
                  ...
			callbacks = mainCallbacks;
		}
		return callbacks;
	}

这个就是具体的service类的代理类了。

6.开始最初的方法调用,会进入DynamicAdvisedInterceptor的intercept方法

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Class<?> targetClass = null;
			Object target = null;
			try {
				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 = getTarget();
				if (target != null) {
					targetClass = target.getClass();
				}
				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() && Modifier.isPublic(method.getModifiers())) {
					// 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 = methodProxy.invoke(target, args);
				}
				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) {
					releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}
this.advised.exposeProxy这个属性是本文的重点,是否暴露代理,默认是false。这个方法会获取具体的操作对象,以及getInterceptorsAndDynamicInterceptionAdvice家在这个切点的所有拦截器,最后组装成一个CglibMethodInvocation开始进行处理,retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();然后释放资源,恢复以前的的面貌,返回结果。

7.开始拦截器的调用proceed,先判断是否执行完全部的拦截器,是的话就执行目标方法

public 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) {
............................
		}
		else {
			// It's an interceptor, so we just invoke it: The pointcut will have
			// been evaluated statically before this object was constructed.
			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
		}
	}
8.这里主要介绍TransactionInterceptor
@Override
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
			@Override
			public Object proceedWithInvocation() throws Throwable {
				return invocation.proceed();
			}
		});
	}

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
			throws Throwable {
		// If the transaction attribute is null, the method is non-transactional.
		final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
		final String joinpointIdentification = methodIdentification(method, targetClass);
		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			Object retVal = null;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
				cleanupTransactionInfo(txInfo);
			}
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}
.......
}
getTransactionAttributeSource().getTransactionAttribute(method, targetClass)从这里取出方法对应的事务配置信息
for (String mappedName : this.nameMap.keySet()) {
				if (isMatch(methodName, mappedName) &&
						(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
					attr = this.nameMap.get(mappedName);
					bestNameMatch = mappedName;
				}
			}
这里根据最少匹配规则选出xml对应的配置信息。
determineTransactionManager(txAttr)选择具体的事务管理器,也就是解析你使用的是Jta还是DataSource。
9.创建事务信息createTransactionIfNecessary,完成之后就会把当前事务信息TransactionInfo绑定到这个线程里(ThreadLocal)以供下次可能使用,
protected TransactionInfo createTransactionIfNecessary(
			PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {

		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
				status = tm.getTransaction(txAttr);
			}
			
		}
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}

获取事务状态信息

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
		Object transaction = doGetTransaction();

		if (definition == null) {
			// Use defaults if no transaction definition given.
			definition = new DefaultTransactionDefinition();
		}

		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}

		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
			definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
			}
			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException ex) {
				resume(null, suspendedResources);
				throw ex;
			}
			catch (Error err) {
				resume(null, suspendedResources);
				throw err;
			}
		}
		else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
		}
	}
首先从doGetTransaction线程私有的ThreadLocal获取已经存在的事务对象DataSourceTransactionObject,然后判断有没有具体配置的方法事务配置DefaultTransactionDefinition,没有的话就是用默认的,然后判断isExistingTransaction这个事务对象中是否存在已有的事务链接信息,

10.下来就是事务传播级别起作用的时候了,

int PROPAGATION_REQUIRED = 0;支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
int PROPAGATION_SUPPORTS = 1;支持当前事务,如果当前没有事务,就以非事务方式执行。
int PROPAGATION_MANDATORY = 2;支持当前事务,如果当前没有事务,就抛出异常。
int PROPAGATION_REQUIRES_NEW = 3;新建事务,如果当前存在事务,把当前事务挂起。外层事务失败回滚的话里面新建的那个事务不会回滚
int PROPAGATION_NOT_SUPPORTED = 4;以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
int PROPAGATION_NEVER = 5;以非事务方式执行,如果当前存在事务,则抛出异常。
int PROPAGATION_NESTED = 6;支持当前事务,如果当前事务存在,则执行一个嵌套事务(外层事务失败回滚的话里面新建的那个事务也会回滚,因为他设置了保存点Savepoint),
如果当前没有事务,就新建一个事务。

根据这7个级别分别挂起使用事务。然后retVal = invocation.proceedWithInvocation();进行下一个拦截器的调用。DefaultTransactionStatus事

	if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
务状态有个特别重要的属性private final boolean newTransaction;它关系到是否提交现在这个事务,当方法具体执行完毕之后就会开始回滚或者提交事务信息,提交事务发生在AbstractPlatformTransactionManager的processCommit,这个值的具体设置在每个传播级别里面,所以就会影响到具体事务的提交。

                               if (status.isNewTransaction()) {
					doCommit(status);
				}
三:原因解析

因为上面这些事务拦截器处理是针对一个具体代理的,但是最初的案例从别的地方开始执行Test1Service.test()时,他其实执行的是Test1Service的代理对象,所以就会执行上面的整个流程,但是刚开始的时候没有事务包裹,因为是PROPAGATION_SUPPORTS ,①执行的时候会自己建立一个新事务,完事之后就会提交,后面发生任何异常都不会影响它。等到执行到this.add()时也就会把他当做一个内部的普通方法调用,而不会把他当成一个具体的对象需要代理,但是如果执行到test2Service.add();②时候,就会执行test2Service的代理也就会创建一个完整的新事务,因为他已经显式表明了我就是test2Service代理对象,最后事务也就会提交成功,到达这个时候int i = 1 / 0;虽然会抛出一个错误,但是他的外层方法并没有包裹事务,而前面的②自己有一个单独的事务,所以他失败也不会影响到②。

四:解决办法

这种代码本身就是错误的,但是不排除有这种特殊的需求,如果是这样的话,spring事务处理设置了一个开关进行代理暴露  <aop:config expose-proxy="true"/>这个配置对应到

	if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
这里就会把正在执行的代理设置到与线程相关的ThreadLocal中, 然后在代码里获取线程绑定的这个代理进行显式调用即可,((TestService) AopContext.currentProxy()).add();
这个时候当代吗执行到这里时,就不会把他当做内部的普通方法调用,而是把它作为一个单独完整的代理进行处理,这个时候他们也就会被包裹到一个事务。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值