spring 事务背后的故事

Posted 汪小哥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 事务背后的故事相关的知识,希望对你有一定的参考价值。

一、spring 事务配置

声明式事务有两种方式,一种是在配置文件(xml)中做相关的事务规则声明,另一种是基于 @Transactional 注解的方式。

1.1 xml 声明 事务

1.1.1 单一的对于目标类进行代理

<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
     abstract="true">
   <property name="transactionManager" ref="transactionManager"/>
   <property name="transactionAttributes">
     <props>
       <prop key="insert*">PROPAGATION_REQUIRED</prop>
       <prop key="update*">PROPAGATION_REQUIRED</prop>
       <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
     </props>
   </property>
 </bean>

 <bean id="myProxy" parent="baseTransactionProxy">
   <property name="target" ref="myTarget"/>
 </bean>

 <bean id="yourProxy" parent="baseTransactionProxy">
   <property name="target" ref="yourTarget"/>
 </bean>

1.1.2 自动代理时代

单一的针对bean 配置非常的麻烦, Auto proxy creator 自动诞生!

BeanNameAutoProxyCreator 根据配置的bean 名称进行自动代理

org.springframework.aop.framework.autoproxy. BeanNameAutoProxyCreator

<bean id="transactionInterceptor"
          class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="transactionManager"/>
        <!-- 配置事务属性 -->
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
            </props>
        </property>
    </bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="beanNames">
        <list>
            <value>*Service*</value>
        </list>
    </property>
    <property name="interceptorNames">
        <list>
            <value>transactionInterceptor</value>
        </list>
    </property>
</bean>

通用的AOP基于代理的解决方案(BeanPostProcessor)


org.springframework.aop.framework.autoproxy. DefaultAdvisorAutoProxyCreator
BeanPostProcessor implementation that creates AOP proxies based on all candidate Advisors in the current BeanFactory. This class is completely generic; it contains no special code to handle any particular aspects, such as pooling aspects.
AOP=Advisors(AOP 具体做的事情)+PointCut(基于方法名称?基于注解?基于表达式?)

 <bean id="transactionInterceptor"
          class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="transactionManager"/>
        <!-- 配置事务属性 -->
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
            </props>
        </property>
    </bean>
<bean id="transactionAttributeSourceAdvisor"
        class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
    <!--事务处理Advisor-->
    <property name="transactionInterceptor" ref="transactionInterceptor"/>
</bean>

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
    <!--自动代理,只针对bean名称为transactionAttributeSource前缀的bean-->
    <property name="advisorBeanNamePrefix" value="transactionAttributeSource"/>
</bean>

处理 aop:config

org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator
aop:config标签

<tx:advice id="transactionInterceptorAdvice">
    <!--spring.framework.transaction.interceptor.TransactionInterceptor-->
    <tx:attributes>
        <tx:method name="*" rollback-for="Exception"/>
        <tx:method name="find*" rollback-for="Exception" read-only="true"/>
        <tx:method name="get*" rollback-for="Exception" read-only="true"/>
    </tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
    <!--org.springframework.aop.config.AopNamespaceHandler 命名空间处理-->
    <!--org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator 处理自动代理-->
    <aop:pointcut id="id_point_cut"  expression="execution(* com.myapp.service.*.*(..)) and !(@annotation(org.springframework.transaction.annotation.Transactional) or @within(org.springframework.transaction.annotation.Transactional)))"/>
    <aop:advisor pointcut-ref="id_point_cut" advice-ref="transactionInterceptorAdvice"/>
</aop:config>

1.2 注解@Transactional 事务

1.2.1 注解事务支持@Transactional

仔细看代码,发现这样的自动创建逻辑非常多的类,最终只有一个自动创建代理去处理这些信息! org.springframework.aop.config.AopConfigUtils 中有判断优先级,判断从低到高最终只有一个自动代理创建!因为最终创建的bean的名称都是 “org.springframework.aop.config.internalAutoProxyCreator" 意味着最终只有一个优先级比较高的自动代理进行创建代理活动!针对所有的advisor

static 
		APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
		APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);


org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator

<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true">
    <!--org.springframework.transaction.config.TxNamespaceHandler 命名空间处理-->
    <!--or @EnableTransactionManagement 注解支持-->
    <!--org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration-->
</tx:annotation-driven>

1.2.2 @AspectJ 注解支持

顺带过一下@AspectJ的支持,也是采用自动代理!
org.springframework.aop.aspectj.annotation. AnnotationAwareAspectJAutoProxyCreator
通过aop命名空间的 aop:aspectj-autoproxy 声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被aop:aspectj-autoproxy隐藏起来了 or 使用 @EnableAspectJAutoProxy

<aop:aspectj-autoproxy
    <!--org.springframework.aop.aspectj.annotation. AnnotationAwareAspectJAutoProxyCreator-->
/>

二、spring 事务的核心

上面讲解了很多,无论是声明方式的事务还是注解方式的事务!最终都是通过封装为AOP代理去处理!转交给事务拦截器去处理 org.springframework.transaction.interceptor.TransactionInterceptor,事务核心就是事务管理器 PlatformTransactionManager 和 事务配置的相关属性,无论是xml and 注解 实质都是转化为了 TransactionAttributeSource 事务属性->TransactionAttribute. 某个方法上是否能够被当前AOP管理呢?

/**
* 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);


public interface TransactionAttributeSource 
/**
    * Return the transaction attribute for the given method,
    * or @code null if the method is non-transactional.
    * @param method the method to introspect
    * @param targetClass the target class (may be @code null,
    * in which case the declaring class of the method must be used)
    * @return TransactionAttribute the matching transaction attribute,
    * or @code null if none found
    */
@Nullable
TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass);

2.1 xml -> TransactionAttribute


xml 命令空间解析事务属性
org.springframework.transaction.config.TxAdviceBeanDefinitionParser


xml 解析 TransactionAttribute
org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource


根据方法的名称来判断当前是否满足

RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
attributeSourceDefinition.getPropertyValues().addPropertyValue(NAME_MAP, transactionAttributeMap);

2.2 注解 -> TransactionAttribute

org.springframework.transaction.annotation.AnnotationTransactionAttributeSource 注解解析事务
这个注解的解析过程是非常头疼的!4.0x 版本,5.x 版本的处理不一样 主要差距在 这个类 org.springframework.transaction.annotation.SpringTransactionAnnotationParser 你必须关心这个,不然使用的时候难免采坑**!最好的方式就是直接在具体的类上标识事务注解!不会采坑!**

  • 2.x 版本 ae.getAnnotation(Transactional.class); 只获取当前元素的注解(或者继承过来的)

  • 4.x 版本 AnnotatedElementUtils.getMergedAnnotationAttributes 只查找当前元素以及元注解(如果是类有继承 @Inherited注解 继承)

  • 5.x 版本 AnnotatedElementUtils.findMergedAnnotationAttributes 查找当前类->遍历元注解查找当前注解->查找当前元素的所有接口->查找当前父类方法

public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) 
		AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
				ae, Transactional.class, false, false);
		if (attributes != null) 
			return parseTransactionAnnotation(attributes);
		
		else 
			return null;
		

2.2.1 获取注解 TransactionAttribute 计算

org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource#computeTransactionAttribute
在这个过程中先去 查询 target 方法上面的去查询… 然后targer class、… 不过随着版本的不同在解析注解的SpringTransactionAnnotationParser 中使用了不同的方式,这里spring 自定义 AliasFor注解、元注解、通过动态代理实现、 AliasFor熟悉覆盖等等更多查看Spring 注解编程模型 get and find 搜索的范围不一样,已经不是JDK 自带的功能了,提供了 AliasFor 属性覆盖,类似继承(find)等等功能!要理解AnnotatedElementUtils 功能才能更好的理解 不同的事务是否生效,采用了哪里的配置!

private TransactionAttribute computeTransactionAttribute(Method method, Class targetClass) 
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) 
        return null;
    

    // The method may be on an interface, but we need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
    // If we are dealing with method with generic parameters, find the original method.
    if (JdkVersion.isAtLeastJava15()) 
        specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
    

    // First try is the method in the target class.
    TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
    if (txAtt != null) 
        return txAtt;
    

    // Second try is the transaction attribute on the target class.
    txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
    if (txAtt != null) 
        return txAtt;
    

    if (specificMethod != method) 
        // Fallback is to look at the original method.
        txAtt = findTransactionAttribute(method);
        if (txAtt != null) 
            return txAtt;
        
        // Last fallback is the class of the original method.
        return findTransactionAttribute(method.getDeclaringClass());
    
    return null;
	

2.2.2 继承注解计算示范

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public class AnnotationsService 

    @Transactional(propagation = Propagation.SUPPORTS)
    public void test() 

    



public class AnnotationsServiceImpl extends AnnotationsService 

    @Override
    public void test() 

    


@Test
public void testServices() throws Exception 
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTargetClass(AnnotationsServiceImpl.class);
    proxyFactory.setTarget(new AnnotationsServiceImpl());
    Object proxy = proxyFactory.getProxy();

    Class<?> targetClass = AopUtils.getTargetClass(proxy);

    Method test = ReflectionUtils.findMethod(targetClass, "test");

    AnnotationTransactionAttributeSource attributeSource = new AnnotationTransactionAttributeSource(true);

    TransactionAttribute transactionAttribute = attributeSource.getTransactionAttribute(test, targetClass);

    log.info("transactionAttribute=", transactionAttribute.toString());



//5.x 22:14:41.184 [main] INFO SpringTest - transactionAttribute=PROPAGATION_SUPPORTS,ISOLATION_DEFAULT; ''
//4.x  22:17:01.467 [main] INFO com.annotations.spring.test.SpringTest - transactionAttribute=PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,-java.lang.Exception


5.0x 过程分析!(最好自己debug 一下)
AnnotatedElementUtils.findMergedAnnotationAttributes 查找当前类->遍历元注解查找当前注解->查找当前元素的所有接口->查找当前父类方法
在查找当前方法 First try is the method in the target class. 过程中 对于查找当前父类方法 命中


4.0x 过程分析 ****(最好自己debug 一下)
AnnotatedElementUtils.getMergedAnnotationAttributes 只查找当前元素以及元注解(如果是类有继承 @Inherited注解 继承)
这个是在 Second try is the transaction attribute on the target class. 这个部分找到的!
通过这个例子,自己可以手动尝试一下子!

2.2.3 继承注解失败计算示范

将上面的 AnnotationsService class 修改为 接口,其他的测试不变

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public interface AnnotationsService 

    @Transactional(propagation = Propagation.SUPPORTS)
    public void test();


public class AnnotationsServiceImpl implements AnnotationsService 

    @Override
    public void test() 

    


修改为接口之后发现5.x 不变,4.0x 为空? 为什么?
AnnotatedElementUtils.getMergedAnnotationAttributes
依然在这里获取数据 Second try is the transaction attribute on the target class.但是这里不继承接口的注解了 so 什么都没有,@Inherited 元注解表示被标注过的 class 的子类所继承。 但注意:一是类并不从它所实现的接口继承;二是方法并不从它所重载的方法继承。更多详见 java @Inherited注解的作用
根据两个例子是否发现十分的神奇?更多查看Spring 注解编程模型 详细了解 AnnotatedElementUtils and AnnotatedUtils 背后的故事。

这里 5.x 中父类的方法和子类的方法 必须 参数类型一样、方法名称一样才能获取到父接口中的注解!
反例 **boolean **doWork(T params) 父类是泛型,子类实现了也是不能继承的。

2.24 对于AnnotatedElementUtils 处理类似继承的理解

我一直理解不了继承这个概念,按照对于继承的理解,应该是将父类的方法熟悉一丢丢的东西都继承过来?


@Inherited 注解的解释: Indicates that an annotation type is automatically inherited. If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class’s superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found, or the top of the class hierarchy (Object) is reached. 这个继承的前提是如果类上面没声明这个注解,才会继承过来! @Transactional 是一个被标注了@Inherited的注解,由于子类存在,并不会从父类继承相关的熟悉!因此这个的结果就是 @Transactional(propagation = Propagation.SUPPORTS)
而不是我yy的@Transactional(rollbackFor = Exception.class, propagation = Propagation.SUPPORTS)


有了这个理解,看懂AnnotatedElementUtils and AnnotatedUtils的代码容易一点,基于JDK 动态代理实现了一套类似继承,AliasFor注解、元注解、熟悉覆盖(两者的区别)

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public class AnnotationsService 

    public void test() 

    



@Transactional(propagation = Propagation.SUPPORTS)
public class AnnotationsServiceImpl extends AnnotationsService 
    
    @Override
    public void test() 

    

更多

系列文章一: spring 注解 事务,声明事务共存—有bug
系列文章二:spring 注解 事务,声明事务混用–解决问题
系列文章三:spring 事务采坑-xml注解 事务混用
系列文章四: spring 事务背后的故事
更多汪小哥

以上是关于spring 事务背后的故事的主要内容,如果未能解决你的问题,请参考以下文章

浅谈Spring中事务管理器

Spring 事务管理

更好的 java 重试框架 sisyphus 背后的故事

#聊一聊悟空编辑器#spring框架事务相关知识点Spring框架教程

小程序音视频背后的故事

Firestore是否提供更多的锁定事务,比如表到表的概念?