基於註解的聲明式事務的實現原理

咱們知道,基於註解的聲明式事務要想生效,必不可少的一步是在容器配置類上加@EnableTransactionManagement註解,開啓事務,因此就從這個註解開始分析。html

 

1. @EnableTransactionManagement註解經過導入方式,在容器中註冊了兩個重要組件:java

   AutoProxyRegistrar——至關於一個自定義組件註冊器,在容器中註冊了一個後置處理器;spring

   ProxyTransactionManagementConfiguration——是一個容器配置類,在其中註冊了事務加強器。app

   

如下是@EnableTransactionManagement註解的源碼,注意@Import中的那個類,實現了ImportsSelector接口,正是經過這個ImportSelector導入了上述兩個組件。關於ImportsSelector的用法https://www.cnblogs.com/dubhlinn/p/10662763.html中有過記錄。less

@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. * <p><b>The default is {@link AdviceMode#PROXY}.</b> * Please note that proxy mode allows for interception of calls through the proxy * only. Local calls within the same class cannot get intercepted that way; an * {@link Transactional} annotation on such a method within a local call will be * ignored since Spring's interceptor does not even kick in for such a runtime * scenario. For a more advanced mode of interception, consider switching this to * {@link AdviceMode#ASPECTJ}. */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * <p>The default is {@link Ordered#LOWEST_PRECEDENCE}. */
    int order() default Ordered.LOWEST_PRECEDENCE; }

 

2. AutoProxyRegistrar組件ide

首先看spring源碼ui

public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar { private final Log logger = LogFactory.getLog(getClass()); /** * Register, escalate, and configure the standard auto proxy creator (APC) against the * given registry. Works by finding the nearest annotation declared on the importing * {@code @Configuration} class that has both {@code mode} and {@code proxyTargetClass} * attributes. If {@code mode} is set to {@code PROXY}, the APC is registered; if * {@code proxyTargetClass} is set to {@code true}, then the APC is forced to use * subclass (CGLIB) proxying. * <p>Several {@code @Enable*} annotations expose both {@code mode} and * {@code proxyTargetClass} attributes. It is important to note that most of these * capabilities end up sharing a {@linkplain AopConfigUtils#AUTO_PROXY_CREATOR_BEAN_NAME * single APC}. For this reason, this implementation doesn't "care" exactly which * annotation it finds -- as long as it exposes the right {@code mode} and * {@code proxyTargetClass} attributes, the APC can be registered and configured all * the same. */ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { boolean candidateFound = false; Set<String> annoTypes = importingClassMetadata.getAnnotationTypes(); for (String annoType : annoTypes) { AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType); if (candidate == null) { continue; } Object mode = candidate.get("mode"); Object proxyTargetClass = candidate.get("proxyTargetClass"); if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() && Boolean.class == proxyTargetClass.getClass()) { candidateFound = true; if (mode == AdviceMode.PROXY) { AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry); if ((Boolean) proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); return; } } } } if (!candidateFound && logger.isInfoEnabled()) { String name = getClass().getSimpleName(); logger.info(String.format("%s was imported but no annotations were found " +
                    "having both 'mode' and 'proxyTargetClass' attributes of type " +
                    "AdviceMode and boolean respectively. This means that auto proxy " +
                    "creator registration and configuration may not have occurred as " +
                    "intended, and components may not be proxied as expected. Check to " +
                    "ensure that %s has been @Import'ed on the same class where these " +
                    "annotations are declared; otherwise remove the import of %s " +
                    "altogether.", name, name, name)); } } }

重點是它實現了ImportBeanDefinitionRegistrar接口,它跟上述ImportsSelector同樣,都是導入方式註冊bean時能夠選用的接口,那麼它在容器中註冊了什麼組件呢?跟進源碼中的粉色粗體語句,會發現它會調用AopConfigUtils的這個方法this

@Nullable public static BeanDefinition registerAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) { return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source); }

 

也就是說,它會在容器中註冊InfrastructureAdvisorAutoProxyCreator,這是個什麼東西?來看一下它的繼承關係圖spa

它實現了SmartInstantiationAwareBeanPostProcessor,說明這是一個後置處理器,並且跟springAOP開啓@EnableAspectJAutoProxy時註冊的AnnotationAwareAspectJProxyCreator實現的是同一個接口(見https://www.cnblogs.com/dubhlinn/p/10708454.html),因此說,聲明式事務是springAOP思想的一種應用。scala

 

3. ProxyTransactionManagementConfiguration組件

先看spring源碼

@Configuration public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration { @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); advisor.setTransactionAttributeSource(transactionAttributeSource()); //① advisor.setAdvice(transactionInterceptor()); //② if (this.enableTx != null) { advisor.setOrder(this.enableTx.<Integer>getNumber("order")); } return advisor; } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionAttributeSource transactionAttributeSource() { return new AnnotationTransactionAttributeSource(); } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) public TransactionInterceptor transactionInterceptor() { TransactionInterceptor interceptor = new TransactionInterceptor(); interceptor.setTransactionAttributeSource(transactionAttributeSource()); if (this.txManager != null) { interceptor.setTransactionManager(this.txManager); } return interceptor; } }

從源碼中能夠看出,ProxyTransactionManagementConfiguration是一個容器配置類,它註冊了一個組件transactionAdvisor,咱們稱爲事務加強器;

而後在這個事務加強器中又注入了兩個屬性:

①:transactionAttributeSource,即屬性解析器;

②:transactionInterceptor,即事務攔截器。

 

首先來看屬性解析器,截取一段源碼

public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable { private static final boolean jta12Present; private static final boolean ejb3Present; static { ClassLoader classLoader = AnnotationTransactionAttributeSource.class.getClassLoader(); jta12Present = ClassUtils.isPresent("javax.transaction.Transactional", classLoader); ejb3Present = ClassUtils.isPresent("javax.ejb.TransactionAttribute", classLoader); } private final boolean publicMethodsOnly; private final Set<TransactionAnnotationParser> annotationParsers; /** * Create a default AnnotationTransactionAttributeSource, supporting * public methods that carry the {@code Transactional} annotation * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation. */
    public AnnotationTransactionAttributeSource() { this(true); } /** * Create a custom AnnotationTransactionAttributeSource, supporting * public methods that carry the {@code Transactional} annotation * or the EJB3 {@link javax.ejb.TransactionAttribute} annotation. * @param publicMethodsOnly whether to support public methods that carry * the {@code Transactional} annotation only (typically for use * with proxy-based AOP), or protected/private methods as well * (typically used with AspectJ class weaving) */
    public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) { this.publicMethodsOnly = publicMethodsOnly; if (jta12Present || ejb3Present) { this.annotationParsers = new LinkedHashSet<>(4); this.annotationParsers.add(new SpringTransactionAnnotationParser()); if (jta12Present) { this.annotationParsers.add(new JtaTransactionAnnotationParser()); } if (ejb3Present) { this.annotationParsers.add(new Ejb3TransactionAnnotationParser()); } } else { this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser()); } } }

屬性解析器有一個成員變量是annotationParsers,是一個集合,能夠添加多種註解解析器(TransactionAnnotationParser),例如spring的、jta的、ejb的,如今咱們只看spring的註解解析器源碼:

public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable { protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) { RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute(); Propagation propagation = attributes.getEnum("propagation"); rbta.setPropagationBehavior(propagation.value()); Isolation isolation = attributes.getEnum("isolation"); rbta.setIsolationLevel(isolation.value()); rbta.setTimeout(attributes.getNumber("timeout").intValue()); rbta.setReadOnly(attributes.getBoolean("readOnly")); rbta.setQualifier(attributes.getString("value")); List<RollbackRuleAttribute> rollbackRules = new ArrayList<>(); for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } for (String rbRule : attributes.getStringArray("rollbackForClassName")) { rollbackRules.add(new RollbackRuleAttribute(rbRule)); } for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } for (String rbRule : attributes.getStringArray("noRollbackForClassName")) { rollbackRules.add(new NoRollbackRuleAttribute(rbRule)); } rbta.setRollbackRules(rollbackRules); return rbta; } }

注意粉色粗體部分,全都是@Transactional註解的屬性,因此屬性解析器的做用之一就是用來解析@Transaction註解的。

 

而後來看一下事務攔截器,仍是截取一段spring源碼

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable { /** * Create a new TransactionInterceptor. * <p>Transaction manager and transaction attributes still need to be set. * @see #setTransactionManager * @see #setTransactionAttributes(java.util.Properties) * @see #setTransactionAttributeSource(TransactionAttributeSource) */
    public TransactionInterceptor() { }/** * Create a new TransactionInterceptor. * @param ptm the default transaction manager to perform the actual transaction management * @param tas the attribute source to be used to find transaction attributes * @see #setTransactionManager * @see #setTransactionAttributeSource(TransactionAttributeSource) */
    public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) { setTransactionManager(ptm); setTransactionAttributeSource(tas); } @Override @Nullable public Object invoke(MethodInvocation invocation) throws Throwable { // Work out the target class: may be {@code null}. // The TransactionAttributeSource should be passed the target class // as well as the method, which may be from an interface.
        Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null); // Adapt to TransactionAspectSupport's invokeWithinTransaction...
        return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed); } }

須要注意的兩點:

① 事務攔截器實現了MethodInterceptor接口,這又是在springAOP中提到的攔截器鏈(https://www.cnblogs.com/dubhlinn/p/10708454.html),追溯一下上面提到的InfrastructureAdvisorAutoProxyCreator後置處理器,它會在代理對象執行目標方法的時候獲取其攔截器鏈,而攔截器鏈就是這個TransactionInterceptor,這就把這兩個組件聯繫起來了;

② 構造方法傳入PlatformTransactionManager(事務管理器)、TransactionAttributeSource(屬性解析器),可是追溯一下上面貼的ProxyTransactionManagementConfiguration的源碼,在註冊事務攔截器的時候並無調用這個帶參構造方法,而是調用的無參構造方法,而後再調用set方法注入這兩個屬性,效果也是同樣的。

 

而後咱們繼續跟進粉色粗體的invokeWithinTransaction方法的源碼,看看觸發的方法裏面執行了什麼

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation) throws Throwable { // If the transaction attribute is null, the method is non-transactional. TransactionAttributeSource tas = getTransactionAttributeSource(); //① final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null); final PlatformTransactionManager tm = determineTransactionManager(txAttr); //② final String joinpointIdentification = methodIdentification(method, targetClass, txAttr); 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 { ... } }

重點看粉色粗體的4個語句

① 獲取屬性解析器,即在ProxyTransactionManagementConfiguration容器配置類中註冊事務攔截器時注入的;

② 獲取事務管理器,跟進一下源碼

protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) { // Do not attempt to lookup tx manager if no tx attributes are set if (txAttr == null || this.beanFactory == null) { return getTransactionManager(); } String qualifier = txAttr.getQualifier(); if (StringUtils.hasText(qualifier)) { return determineQualifiedTransactionManager(this.beanFactory, qualifier); } else if (StringUtils.hasText(this.transactionManagerBeanName)) { return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName); } else { PlatformTransactionManager defaultTransactionManager = getTransactionManager(); if (defaultTransactionManager == null) { defaultTransactionManager = this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY); if (defaultTransactionManager == null) { defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class); this.transactionManagerCache.putIfAbsent( DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager); } } return defaultTransactionManager; } }

spring會先根據qualifier,即@Transactional註解的value屬性去獲取,可是這個咱們通常都不配置,咱們只要看到粉色粗體部分就放心了,只要在容器中有註冊過PlatformTransactionManager類型的事務管理器,就能夠直接經過類型來獲取;

③ 若是目標方法拋異常,會執行completeTransactionAfterThrowing,跟進一下源碼:

protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) { if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex); } if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) { try { txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } else { // We don't roll back on this exception. // Will still roll back if TransactionStatus.isRollbackOnly() is true. try { txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by commit exception", ex); ex2.initApplicationException(ex); throw ex2; } catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by commit exception", ex); throw ex2; } } } }

核心是粉色粗體語句——拿到事務管理器、執行回滾;

④ 若是目標方法正常運行,則會執行commitTransactionAfterReturning,一樣跟進一下源碼:

protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) { if (txInfo != null && txInfo.getTransactionStatus() != null) { if (logger.isTraceEnabled()) { logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); } txInfo.getTransactionManager().commit(txInfo.getTransactionStatus()); } }

粉色粗體部分——拿到事務管理器、執行提交。

 

總結一下基於註解的聲明式事務的原理:

1. 在容器配置類上使用@EnableTransactionManagement註解,該註解在容器中註冊了兩大組件——AutoProxyRegistrar、ProxyTransactionManagementConfiguration;

2. AutoProxyRegistrar經過導入方式在容器中註冊了InfrastructureAdvisorAutoProxyCreator,這是一個後置處理器;

3. ProxyTransactionManagementConfiguration自己就是一個容器配置類,它註冊了transactionAdvisor(事務加強器),而後又在這個事務加強器中注入了兩個屬性transactionAttributeSource、transactionInterceptor;

4. transactionAttributeSource用於解析@Transactional註解的各類屬性;

5. transactionInterceptor實現了MethodInterceptor,是一個攔截器鏈,這個攔截器鏈會從容器中獲取事務管理器,利用事務管理器,在目標方法發生異常時執行回滾,在目標發生正常完成後提交事務;

6. 第2步的InfrastructureAdvisorAutoProxyCreator後置處理器,會在目標對象建立完成以後將其包裝爲代理對象,代理對象在執行目標方法時會首先獲取攔截器鏈,這個攔截器鏈就是第5步的transactionInterceptor。

相關文章
相關標籤/搜索