问题描述
今天发现Spring一个问题
在Spring 2.5.6.SEC01、WebSphere 6.1.0.21、aspectj 1.6.4环境下使用注解@Transactional(propagation=Propagation.REQUIRES_NEW) 开启事务竟然 throw TransactionSuspensionNotSupportedException
官方文档写的很清楚,
http://static.springframework.org/spring/docs/2.5.x/reference/transaction.html#transaction-application-server-integration-websphere
原文如下:
9.8.1. IBM WebSphere
On WebSphere 6.0 and above, the recommended Spring JTA transaction manager to use is WebSphereUowTransactionManager. This special adapter leverages IBM's UOWManager API which is available in WebSphere Application Server 6.0.2.19 or above and 6.1.0.9 or above. With this adapter, Spring-driven transaction suspension (suspend/resume as initiated by PROPAGATION_REQUIRES_NEW) is officially supported by IBM!
从上文来看,很明显是支持的,并且使用配置式事务:
<tx:advice id="txAdvice" transaction-manager="txManager"> <tx:attributes> <tx:method name="execute" propagation="REQUIRES_NEW"/> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice>
确实是没有问题的,REQUIRES_NEW被支持
究其原因,原来是 AbstractTransactionAspect.java 如下代码导致
@SuppressAjWarnings("adviceDidNotMatch") before(Object txObject) { transactionalMethodExecution(txObject) MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature(); Method method = methodSignature.getMethod(); TransactionInfo txInfo = createTransactionIfNecessary(method, txObject.getClass()); }
TransactionAspectSupport.java
protected TransactionInfo createTransactionIfNecessary( TransactionAttribute txAttr, final String joinpointIdentification) { // If no name specified, apply method identification as transaction name. if (txAttr != null && txAttr.getName() == null) { txAttr = new DelegatingTransactionAttribute(txAttr) { public String getName() { return joinpointIdentification; } }; } TransactionStatus status = null; if (txAttr != null) { PlatformTransactionManager tm = getTransactionManager(); if (tm != null) { status = tm.getTransaction(txAttr); } else { if (logger.isDebugEnabled()) { logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured"); } } } return prepareTransactionInfo(txAttr, joinpointIdentification, status); }
由于使用的是
是@Before、@AfterReturning 、@AfterThrowing这些Advice来开启、提交、回滚事务
而不是@Around,
没有把 WebSphereUowTransactionManager 作为CallbackPreferringPlatformTransactionManager使用
(未用WebSphereUowTransactionManager.execute进行回调)
从而导致IBM的UOWManager接口根本就没有使用到,
并且WebSphere是不提供javax.transaction.TransactionManager给用户的,
当Spring执行到
org.springframework.transaction.jta.JtaTransactionManager.doJtaResume
时发现getTransactionManager() == null
if (getTransactionManager() == null) { throw new TransactionSuspensionNotSupportedException( "JtaTransactionManager needs a JTA TransactionManager for suspending a transaction: " + "specify the 'transactionManager' or 'transactionManagerName' property"); }
解决方案
使用@Around作为Advice
具体步骤如下
1. 创建@Pointcut
AchievoTransactionAspect.java
package com.achievo.framework.transaction.aspectj; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.Ordered; import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; import org.springframework.transaction.interceptor.AchievoAbstractTransactionAspect; /* * @author xhm(hyamine) * change the aspectj code style class :org.springframework.transaction.aspectj.AnnotationTransactionAspect * to @AspectJ style * Fixed Spring' bug: * when use @Transactional(propagation=Propagation.REQUIRES_NEW) in WebSphere 6.1 as AspectJ mode * will throw TransactionSuspensionNotSupportedException * because it didn't use WebSphereUowTransactionManager as a CallbackPreferringPlatformTransactionManager */ @Aspect public class AchievoTransactionAspect extends AchievoAbstractTransactionAspect implements Ordered { private int order = 200; public AchievoTransactionAspect() { super(new AnnotationTransactionAttributeSource(false)); } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } @Pointcut("execution(public * ((@org.springframework.transaction.annotation.Transactional *)+).*(..)) && @this(org.springframework.transaction.annotation.Transactional)") public void executionOfAnyPublicMethodInAtTransactionalType(){} @Pointcut("execution(* *(..)) && @annotation(org.springframework.transaction.annotation.Transactional)") public void executionOfTransactionalMethod(){} @Pointcut("(executionOfAnyPublicMethodInAtTransactionalType() || executionOfTransactionalMethod()) && this(txObject)") public void transactionalMethodExecution(Object txObject){} //@SuppressAjWarnings("adviceDidNotMatch") }
2. 创建@Around advice
AchievoAbstractTransactionAspect.java
package org.springframework.transaction.interceptor; import java.lang.reflect.Method; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.TransactionAspectSupport; import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttributeSource; import org.springframework.transaction.support.CallbackPreferringPlatformTransactionManager; import org.springframework.transaction.support.TransactionCallback; /* * @author xhm(hyamine) * most of this class code copy from * org.springframework.transaction.interceptor.TransactionInterceptor */ @Aspect public abstract class AchievoAbstractTransactionAspect extends TransactionAspectSupport { protected AchievoAbstractTransactionAspect(TransactionAttributeSource tas) { setTransactionAttributeSource(tas); } @Around("transactionalMethodExecution(txObject)") public Object useTransaction(ProceedingJoinPoint thisJoinPoint,Object txObject) throws Throwable{ Object result = null; Class targetClass = txObject.getClass(); MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature(); Method method = methodSignature.getMethod(); final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass); final String joinpointIdentification = methodIdentification(method); final ProceedingJoinPoint invocation = thisJoinPoint; if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) { // Standard transaction demarcation with getTransaction and commit/rollback calls. TransactionInfo txInfo = createTransactionIfNecessary(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.proceed(); } catch (Throwable ex) { // target invocation exception completeTransactionAfterThrowing(txInfo, ex); throw ex; } finally { cleanupTransactionInfo(txInfo); } commitTransactionAfterReturning(txInfo); return retVal; } else { // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr, new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status); try { return invocation.proceed(); } catch (Throwable ex) { if (txAttr.rollbackOn(ex)) { // A RuntimeException: will lead to a rollback. if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } else { throw new ThrowableHolderException(ex); } } else { // A normal return value: will lead to a commit. return new ThrowableHolder(ex); } } finally { cleanupTransactionInfo(txInfo); } } }); // Check result: It might indicate a Throwable to rethrow. if (result instanceof ThrowableHolder) { throw ((ThrowableHolder) result).getThrowable(); } else { return result; } } catch (ThrowableHolderException ex) { throw ex.getCause(); } } // result = thisJoinPoint.proceed(); // return result; } @Pointcut public abstract void transactionalMethodExecution(Object txObject); private static class ThrowableHolder { private final Throwable throwable; public ThrowableHolder(Throwable throwable) { this.throwable = throwable; } public final Throwable getThrowable() { return this.throwable; } } private static class ThrowableHolderException extends RuntimeException { public ThrowableHolderException(Throwable throwable) { super(throwable); } public String toString() { return getCause().toString(); } } }
3. 修改Aspectj配置文件META-INF/aop.xml(classpath下)
<aspects> <aspect name="com.achievo.framework.transaction.aspectj.AchievoTransactionAspect"/> <!-- <aspect name="org.springframework.transaction.aspectj.AnnotationTransactionAspect"/> --> </aspects>
4. 修改spring 配置文件
<!-- <tx:annotation-driven transaction-manager="transactionManager" mode="aspectj" order="100"/> --> <bean id="achievoTransaction" class="com.achievo.framework.transaction.aspectj.AchievoTransactionAspect" factory-method="aspectOf"> <property name="transactionManager" ref="transactionManager" /> <property name="order" value="100"/> </bean>