Spring 事务

1.什么是事务

保证业务操作完整性的一种数据库机制

2.事务的ACID

  • A:原子性,多次操作要不一起成功,腰部一起失败(部分失败为savepoint)
  • C:一致性,事务开始时数据状态,事务结束时状态数据一致
  • I:隔离性,多个事务不能互相影响,做到隔离
  • D:持久性,事务操作的结果,永久持久化到数据库或者其他存储介质当中

3.事务处理

单机版事务处理(Spring支持)

1. Connection (JDBC,Hiberate,Mybatis):数据库连接对象
	Hiberate(JPA):Transcation(Connection)
	Mybatis:				SqlSession(Connection)
2. DataSource -> Connection
  connection.setAutoCommit(false)
  connection.commit()
  connection.rollback();

以上是单机版开发的核心步骤,主要是核心是 数据库连接对象(Connection)

问题:在单机版系统中开发传统的MVC分层架构中如何保证 Service DAO层共用同一个连接对象呢

答: 使用ThreadLocal来实现共用一个Connection对象

4.Spring控制事务

核心要点:通过AOP方式来创建事务

控制事务编程
1. 编码 TrancationTemplate
2. 声明式事务:AOP Proxy代理设计模式,通过配置文件或者注解的方式来控制事务

4.1 事务开发的伪代码

  • 通过配置文件的方式,如下
    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dynamicDataSource"/>
    </bean>
		<!-- 配置事务传播特性 -->
    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="insert*" propagation="REQUIRED"/>
            <tx:method name="save*" propagation="REQUIRED"/>
            <tx:method name="delete*" propagation="REQUIRED"/>
            <tx:method name="update*" propagation="REQUIRED"/>
            <tx:method name="recycle*" propagation="REQUIRED"/>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="load*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="query*" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    <!-- 配置参与事务的方法 -->
    <aop:config>
        <aop:advisor pointcut="execution(*.*(..))" advice-ref="transactionAdvice"/>
    </aop:config>

其主要核心是AOP的编程

  • 原始对象
  • 额外功能(增强)
    • 控制事务的代码
      • PlatformTransactionManager:接口,控制事务
        • 子类有:DataSourceTrancationManager(JDBC,Mybatis)、 HibernateTransctionManager(Hibernate JPA)
        • 开启事务
        • 提交事务
        • 回滚事务
    • 事务属性
      • 隔离属性:isolation
      • 传播属性:propagation
      • 只读属性:readonly
      • 超时属性:timeout
      • 异常属性:exception
  • 组转切面

5.事务属性

隔离属性(isolation):隔离属性事务的并发问题

  • ISOLATION_DEFAULT: 数据库默认配置,MYSQL REPEATABLE_READ
  • ISOLATION_READ_UNCOMMITTED:读未提交,读取到另一个事务未提交的数据
  • ISOLATION_READ_COMMITTED:读已提交
  • ISOLATION_REPEATABLE_READ:可重复读
  • ISOLATION_SERIALIZABLE:串行化

TransactionDefinition类中定义
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;


传播属性:propagation

  • REQUIRED:外部没有事务,开启事务;外部存在事务,则融合
  • REQUIRED_NEW:外部没有事务,开启事务;外部存在事务则挂起事务,开启新的事务执行,完成后外部事务继续执行
  • MANDATORY:外部必须存在事务
  • SUPPPORTS:外部没有事务,则不开启事务,外部存在事务,则融合
  • NOT_SUPPORTED:外部没有事务则不开事务,外部有事务,抛出异常
  • NESTED:内嵌事务SAVEPOINT

只读属性:readonly

设置事务为只读,提高事务运行的效率


超时属性:timeout

通过超时属性设置本事务最长等待时间 -1:代表由数据库底层决定等待的时间


异常属性:exception

  • RuntimeException及其子类:默认回滚事务
  • Exception及其子类:默认提交事务

注意⚠️:使用事务属性的目的就是更好的描述事务的特点

5.1 Spring实现了事务属性中的哪几种呢?

Spring底层没有实现ISOLATION READONLY TIMEOUT ,其实现主要由JDBC规范来完成即数据库驱动完成

conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
conn.setReadOnly(true);
conn.setNetworkTimeout();

Spring底层实现了:传播属性:PROPAGATION异常属性:EXCEPTION

6.Spring事务实现的源码浅析

Spring事务实现主要是由AOP来实现,在Spring当中AOP的实现主要由代理实现(JDK动态代理、CGLIB动态代理)
AOP的大致原理如下:

  • 声明切入点:Pointcut-> AspectJExpressionPointcut
  • 声明增强逻辑:Advice -> MethodInterceptor
  • 组装增强和切入点:Advisor-> DefaultPointcutAdvisor

伪代码如下:

 public static void main(String[] args) throws NoSuchMethodException {
   			// 声明切点
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* *())");
				// 声明增强逻辑
        MethodInterceptor methodInterceptor = methodInvocation -> {
            System.out.println("before....");
            methodInvocation.proceed();
            System.out.println("after....");
            return null;
        };
				// 组装切点、增强逻辑
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,methodInterceptor);
				// 通过代理工厂创建目标的代理对象
        final Target1 target1 = new Target1();
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target1);
        factory.addAdvisor(advisor);
        final I1 proxy  = (I1) factory.getProxy();
        proxy.bar();
        proxy.foo();
    }

Spring 选择代理条件

  • proxyTargetClass = false : 目标实现接口,采用JDK实现
  • proxyTargetClass = false : 目标有实现接口,采用CGLIB实现
  • proxyTargetClass = true : 采用CGLIB实现
DefaultPointcutAdvisor#setTargetClass(true | false);

注意⚠️:Spring穿件动态代理的时机主要体现在两处地方依赖注入之前(populate)初始化(initialize)之后

  • 依赖注入之前创建代理主要是解决循环依赖的问题
  • 初始化之后创建代理是为了增强Bean

Spring 事务逻辑

1. Spring事务的底层依附 (DataSource -> Connection -> ThreadLocal -> commit | rollback )
2. 增强额外功能
		DataSourceTransactionMannager(PlatformTransactionManager) : JDBC Mybatis
		TransactionAttributes
		
3. BeanPostProcesser创建代理
4. 调用业务方法的过程当中,动态的将 原始功能 + 事务额外功能整合在一起

1.Spring 激活事务功能注解 EnableTransactionManagement 分析

其为一个组合注解,内部使用@Import注解导入了一个TransactionManagementConfigurationSelector类,Import注解的主要作用也是导入一个Bean到容器中,其解析主要是由ConfigurationClassPostProcessor这个Bean工厂后置处理器来解析执行最终会调用到ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()方法中的ConfigurationClassParser#doProcessConfigurationClass()中解析,在此不多赘述了,详细可以查阅Spring IOC篇章深入学习

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

 
   boolean proxyTargetClass() default false;
   
   AdviceMode mode() default AdviceMode.PROXY;

   int order() default Ordered.LOWEST_PRECEDENCE;

}

2.TransactionManagementConfigurationSelector类解析

public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

   @Override
   protected String[] selectImports(AdviceMode adviceMode) {
      switch (adviceMode) {
         case PROXY:
            return new String[] {AutoProxyRegistrar.class.getName(),
                  ProxyTransactionManagementConfiguration.class.getName()};
         case ASPECTJ:
            return new String[] {determineTransactionAspectClass()};
         default:
            return null;
      }
   }

   private String determineTransactionAspectClass() {
      return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
            TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
            TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
   }

}

adviceMode默认为AdviceMode.PROXY

AutoProxyRegistrar:主要用于解析@EnableTransactionManagement注解属性和注册一个用于事务控制创建代理的BeanPostProcesser,下文详细分析

ProxyTransactionManagementConfiguration:主要是解析事务属性(TranscationAttrbiutes)

6.1 AutoProxyRegistrar类解析

在这里插入图片描述

6.1.1 AopConfigUtils.registerAutoProxyCreatorIfNecessary()方法分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f0WMQQyU-1652277072065)(/Users/qinhan/Library/Application Support/typora-user-images/image-20220511202418933.png)]

6.1.2 InfrastructureAdvisorAutoProxyCreator类分析

类图如下

在这里插入图片描述

6.1.3 AbstractAutoProxyCreator#wrapIfNecessary:方法解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4g8rxAol-1652277072065)(/Users/qinhan/Library/Application Support/typora-user-images/image-20220511203727619.png)]

6.1.4 AbstractAutoProxyCreator#createProxy:方法解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EqxwJYjk-1652277072065)(/Users/qinhan/Library/Application Support/typora-user-images/image-20220511204125507.png)]


6.2 ProxyTransactionManagementConfiguration 类解析

在这里插入图片描述

Aop创建了动态代理对象,动态字节码技术在运行的过程中,动态加入额外的功能

注意⚠️:代理是在运行的过程中,才会把原始对象功能与额外功能进行整合。动态代理的运行效率要相低于静态代理

上文分析是由JDK创建的动态代理,调用我们自己的业务方法时,会到相应的代理CglibAopProxy``JdkDynamicAopProxy中的invoke()方法

详细逻辑如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KohofzuZ-1652277072065)(/Users/qinhan/Library/Application Support/typora-user-images/image-20220511210130304.png)]

invocation.proceed()方法最终会执行到下图所框位置
在这里插入图片描述

由于我们已经声明一个事务拦截器,如下图,所以此处会进入TransactionInterceptor#invoke();方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s20oTOXM-1652277072065)(/Users/qinhan/Library/Application Support/typora-user-images/image-20220511210708352.png)]

6.2.1 TransactionInterceptor#invoke()方法解析
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
      final InvocationCallback invocation) throws Throwable {

   // TransactionAttributeSource 工具类,获取对应方法上事务属性的工具类
   TransactionAttributeSource tas = getTransactionAttributeSource();
  
   // TransactionAttribute 封装了事务属性
   final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
  
   // 事务管理器,DataSourceTrancationManager,在配置Bean中或者配置文件当中,是自己配置的内容
   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 {
       	// 原始方法的执行
         retVal = invocation.proceedWithInvocation();
      }
      catch (Throwable ex) {
         // target invocation exception
         completeTransactionAfterThrowing(txInfo, ex);
         throw ex;
      }
      finally {
         cleanupTransactionInfo(txInfo);
      }
      commitTransactionAfterReturning(txInfo);
      return retVal;
   }

TransactionAttributeSource 工具类,获取对应方法上事务属性的工具类

TransactionAttribute 封装了事务属性

PlatformTransactionManager:事务管理器,DataSourceTrancationManager,在配置Bean中或者配置文件当中,是自己配置的内容

joinpointIdentification:需要控制事务的方法名字

大家看到这里就应该可以发现,事务应该就是在这里被创建的,

retVal = invocation.proceedWithInvocation();这一行是目标方法即我们的业务方法

事务应该在此方法之前进行创建事务,在此方法发生异常之后进行回滚事务,正常执行成功之后进行提交事务

我们下面逐步开始分析

6.2.2 createTransactionIfNecessary(tm, txAttr, joinpointIdentification);方法分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNGtblZQ-1652277072066)(/Users/qinhan/Library/Application Support/typora-user-images/image-20220511212505079.png)]

status = tm.getTransaction(txAttr); 方法含义
 1. 事务的开启 -> Connection.setAutoCommit(false)
 2. 获取事务的状态 -> TransactionStatus
 3. 事务同步相关的状态信息
 		TransactionSynchronizationManager,通过ThreadLocal存储了很多线程同步标志

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q4XDUXuX-1652277072066)(/Users/qinhan/Library/Application Support/typora-user-images/image-20220511214834998.png)]

Spring处理 传播属性相关内容和控制事务
最重要的一个工具类
目的 把与事务相关的一些重要信息,绑定ThrealLocal

待续

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值