spring事务背后的故事(代码片段)

汪小哥 汪小哥     2022-12-14     441

关键词:

一、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 事务背后的故事
更多汪小哥

聊聊cookiesessiontoken背后的故事(代码片段)

...展阶段的产物本文分享自华为云社区《Cookie、Session、Token背后的故事》,作者:龙哥手记。1.网站交互体验升级作为网友的我们,每天都会使用浏览器来逛各种网站,来满足日常的工作生活需求。现在的交互体验... 查看详情

电话发明背后的浪漫爱情故事(代码片段)

闪电的驯服者:电学的历史AlexanderGrahamBellBiography:TheTelephone&ARemarkableDeafWomanNamedMabel 01电话机一、前言  亚历山大·格雷厄姆·贝尔发明并推广电话,是在失聪女人梅布尔·哈伯德的影响下完成的。什么?一个失聪... 查看详情

保护亿万数据安全,spring有“声明式事务”绝招(代码片段)

摘要:点外卖时,你只需考虑如何拼单;选择出行时,你只用想好目的地;手机支付时,你只需要保证余额充足。但你不知道这些智能的背后,是数以亿计的强大数据的支持,这就是数据库的力量... 查看详情

聊聊百度搜索背后的故事(代码片段)

聊聊“吴牙签”背后的搜索引擎技术大家好,我是鱼皮,今天分享点有趣的技术知识。前两天,我想上网买包牙签,于是就打开了某度搜索。结果让我懵逼,我搜到的第一条内容竟然不是拿来剔牙的工具,... 查看详情

咱们从头到尾说一次spring事务管理(器)(代码片段)

先点赞再看,养成好习惯事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一。但除了八股文中需要熟读并背诵的那些个传播行为之外,背后的“为什么”和核心原理更为重要。​写这篇文章之前,我也翻过一... 查看详情

聊一聊前端图片懒加载背后的故事(代码片段)

本文内容什么是懒加载如何实现懒加载监听滚动事件IntersectionObserver浏览器原生方案本文小结相信大家已经注意到我博客有了一点变化,因为博主最近利用空闲时间对博客进行了优化。经过博主的不懈努力,首屏渲染时间... 查看详情

聊一聊前端图片懒加载背后的故事(代码片段)

本文内容什么是懒加载如何实现懒加载监听滚动事件IntersectionObserver浏览器原生方案本文小结相信大家已经注意到我博客有了一点变化,因为博主最近利用空闲时间对博客进行了优化。经过博主的不懈努力,首屏渲染时间... 查看详情

聊一聊前端图片懒加载背后的故事(代码片段)

本文内容什么是懒加载如何实现懒加载监听滚动事件IntersectionObserver浏览器原生方案本文小结相信大家已经注意到我博客有了一点变化,因为博主最近利用空闲时间对博客进行了优化。经过博主的不懈努力,首屏渲染时间... 查看详情

阿里面试挂了,就因为面试官说我spring事务管理(器)不熟练?(代码片段)

前言事务管理,一个被说烂的也被看烂的话题,还是八股文中的基础股之一。但除了八股文中需要熟读并背诵的那些个传播行为之外,背后的“为什么”和核心原理更为重要。​写这篇文章之前,我也翻过一些事务管理器原理介... 查看详情

androidndk——必知必会之androidstudio使用cmake构建ndk项目的背后的故事(代码片段)

文章大纲引言一、AndroidStudio的NDK编译工具二、CMake1、CMake的基本语法1.1、变量定义和引用1.2、command1.3、add_library和add_executable1.4、find_library1.5、target_link_libraries1.6、include_directories1.6、message2、CMake工具链android.toolcha 查看详情

spring事务的传播特性(代码片段)

  spring事务分为本地事务和分布式事务,其中本地事务其实就是数据库事务,Spring事务有三个核心类:TransactionDefinition、PlatformTransactionManager、TransactionStatus。  首先来看事务定义类TransactionDefinition,其中定义了事务的7种传... 查看详情

5.spring事务(代码片段)

Spring事务管理事务原本是数据库中的概念,在Dao层。但一般情况下,需要将事务提升到业务层,即Service层。这样做是为了能够使用事务的特性来管理具体的业务。在Spring中通常可以通过以下两种方式来实现对事务的... 查看详情

5.spring事务(代码片段)

Spring事务管理事务原本是数据库中的概念,在Dao层。但一般情况下,需要将事务提升到业务层,即Service层。这样做是为了能够使用事务的特性来管理具体的业务。在Spring中通常可以通过以下两种方式来实现对事务的... 查看详情

spring事务传播机制详解(代码片段)

Spring的事务,也就是数据库的事务操作,符合ACID标准,也具有标准的事务隔离级别。  但是Spring事务有自己的特点,也就是事务传播机制。   所谓事务传播机制,也就是在事务在多个方法的调用中是如何传递... 查看详情

spring事务管理(代码片段)

文章目录PlatformTransactionManagerTransactionDefinition属性方法SpringAPI事务管理方式使用底层方法管理事务使用TransactionTemplate管理事务Spring声明式管理事务基于TransactionInterceptor的事务管理基于TransactionProxyFactoryBean声明事务基于\\命名空间... 查看详情

spring事务使用最佳实践(代码片段)

目录1Spring事务最佳实践1.1、Spring事务传播机制1.2、隔离规则1.3、Spring事务实现方式1.3.1、编程式事务实现1.3.2、声明式事务实现1.3.3、Spring声明式事务使用注意事项2、事务问题治理2.1、大事物的危害2.1.1事务问题原因分类2.1.2、大... 查看详情

spring配置事务管理器(代码片段)

  在Spring中数据库事务是通过PlatformTransactionManager进行管理的,jdbcTemplate是不能支持事务的,而能够支持事务的是org.springframework.transaction.support.TransactionTemplate模板,它是Spring所提供的事务管理器的模板  •事务的创建、... 查看详情

spring中的事务(代码片段)

Spring中的事务管理声明式事务:AOP编程式事务:需要在代码中,进行事务的管理为什么需要事务?如果不配置事务,可能存在数据提交不一致的情况下;如果我们不在Spring中配置声明式事务,我们就需要在... 查看详情