Spring @Transactional原理
今天想用一下Spring的Transaction Manager,但中间遇到一个问题,但文档上讲得不是很清楚,于是乎只得自己去扒代码来看了。
首先从配置入手,启用Spring的TransactionManagement需要在Configuration Bean上加上@EnableTransactionManagement注解,或者在XML配置文件中加上<tx:annotation-driver />元素来启用事务支持,这样我们将会在Spring Context里面注册事务支持所需要的组件,比如Interceptor, Advisor等等。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { /** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code @Transactional}. For example, other beans marked with Spring's * {@code @Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ boolean proxyTargetClass() default false; /** * Indicate how transactional advice should be applied. The default is * {@link AdviceMode#PROXY}. * @see AdviceMode */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * The default is {@link Ordered#LOWEST_PRECEDENCE}. */ int order() default Ordered.LOWEST_PRECEDENCE; }
上面的又引入TransactionManagementConfigurationSelector配置,从下面TransactionManagementConfigurationSelector的内容可以看出,当我们使用了aspectj的时候,它导入的是org.springframework.transaction.aspectj.AspectJTransactionManagementConfiguration,这里我们重点关注AnnotationTransactionAspect和txManager。txManager从上下文中的TransactionManagementConfigurer(这是一个泛型注入)来获取,其中txAspect.setTransactionManager方法是继承结构上基类TransactionAspectSupport中的方法。
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> { /** * {@inheritDoc} * @return {@link ProxyTransactionManagementConfiguration} or * {@code AspectJTransactionManagementConfiguration} for {@code PROXY} and * {@code ASPECTJ} values of {@link EnableTransactionManagement#mode()}, respectively */ @Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME}; default: return null; } } }
@Configuration public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public AnnotationTransactionAspect transactionAspect() { AnnotationTransactionAspect txAspect = AnnotationTransactionAspect.aspectOf(); if (this.txManager != null) { txAspect.setTransactionManager(this.txManager); } return txAspect; } } @Configuration public abstract class AbstractTransactionManagementConfiguration implements ImportAware { protected AnnotationAttributes enableTx; /** * Default transaction manager, as configured through a {@link TransactionManagementConfigurer}. */ protected PlatformTransactionManager txManager; @Override public void setImportMetadata(AnnotationMetadata importMetadata) { this.enableTx = AnnotationAttributes.fromMap( importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false)); if (this.enableTx == null) { throw new IllegalArgumentException( "@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName()); } } @Autowired(required = false) void setConfigurers(Collection<TransactionManagementConfigurer> configurers) { if (CollectionUtils.isEmpty(configurers)) { return; } if (configurers.size() > 1) { throw new IllegalStateException("Only one TransactionManagementConfigurer may exist"); } TransactionManagementConfigurer configurer = configurers.iterator().next(); this.txManager = configurer.annotationDrivenTransactionManager(); } @Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionalEventListenerFactory transactionalEventListenerFactory() { return new TransactionalEventListenerFactory(); } }
AnnotationTransactionAspect这个类比较奇怪,它是aspect4j格式的AnnotationTransactionAspect.aj编译过来的,下面是它的源码,可见它就是适配aspect4j的,定义拦截点(pointcut)以及拦截操作,拦截点主要是@Transactional标记的方法和类,拦截操作主要是调用TransactionAspectSupport.invokeWithinTransaction方法。
public aspect AnnotationTransactionAspect extends AbstractTransactionAspect { public AnnotationTransactionAspect() { super(new AnnotationTransactionAttributeSource(false)); } /** * Matches the execution of any public method in a type with the * Transactional annotation, or any subtype of a type with the * Transactional annotation. */ private pointcut executionOfAnyPublicMethodInAtTransactionalType() : execution(public * ((@Transactional *)+).*(..)) && @this(Transactional); /** * Matches the execution of any method with the * Transactional annotation. */ private pointcut executionOfTransactionalMethod() : execution(* *(..)) && @annotation(Transactional); /** * Definition of pointcut from super aspect - matched join points * will have Spring transaction management applied. */ protected pointcut transactionalMethodExecution(Object txObject) : (executionOfAnyPublicMethodInAtTransactionalType() || executionOfTransactionalMethod() ) && this(txObject); }
public abstract aspect AbstractTransactionAspect extends TransactionAspectSupport implements DisposableBean { /** * Construct the aspect using the given transaction metadata retrieval strategy. * @param tas TransactionAttributeSource implementation, retrieving Spring * transaction metadata for each joinpoint. Implement the subclass to pass in * {@code null} if it is intended to be configured through Setter Injection. */ protected AbstractTransactionAspect(TransactionAttributeSource tas) { setTransactionAttributeSource(tas); } @Override public void destroy() { clearTransactionManagerCache(); // An aspect is basically a singleton } @SuppressAjWarnings("adviceDidNotMatch") Object around(final Object txObject): transactionalMethodExecution(txObject) { MethodSignature methodSignature = (MethodSignature) thisJoinPoint.getSignature(); // Adapt to TransactionAspectSupport's invokeWithinTransaction... try { return invokeWithinTransaction(methodSignature.getMethod(), txObject.getClass(), new InvocationCallback() { public Object proceedWithInvocation() throws Throwable { return proceed(txObject); } }); } catch (RuntimeException ex) { throw ex; } catch (Error err) { throw err; } catch (Throwable thr) { Rethrower.rethrow(thr); throw new IllegalStateException("Should never get here", thr); } } /** * Concrete subaspects must implement this pointcut, to identify * transactional methods. For each selected joinpoint, TransactionMetadata * will be retrieved using Spring's TransactionAttributeSource interface. */ protected abstract pointcut transactionalMethodExecution(Object txObject); /** * Ugly but safe workaround: We need to be able to propagate checked exceptions, * despite AspectJ around advice supporting specifically declared exceptions only. */ private static class Rethrower { public static void rethrow(final Throwable exception) { class CheckedExceptionRethrower<T extends Throwable> { @SuppressWarnings("unchecked") private void rethrow(Throwable exception) throws T { throw (T) exception; } } new CheckedExceptionRethrower<RuntimeException>().rethrow(exception); } } }
TransactionAspectSupport.invokeWithinTransaction的代码就比较清晰了,获取TransactionManager并调用它的execute方法,传入我们的callback,在callback中执行我们的事务方法。determineTransactionManager方法用于获取TransactionManagaer,当我们没有从TransactionManagementConfigurer中配置目标TransactionManager,通过其它方式确定一个TransactionManager,比如什么都没有配置的时候,默认从上下文(BeanFactory)中获取类型为PlatformTransactionManager的Bean作为默认的TransactionManager
public class TransactionAspectSupport { ..... ..... /** * General delegate for around-advice-based subclasses, delegating to several other template * methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager} * as well as regular {@link PlatformTransactionManager} implementations. * @param method the Method being invoked * @param targetClass the target class that we're invoking the method on * @param invocation the callback to use for proceeding with the target invocation * @return the return value of the method, if any * @throws Throwable propagated from the target invocation */ 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; } else { // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. try { Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status); try { return invocation.proceedWithInvocation(); } 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(); } } } ..... ..... }