Spring-事务

122 阅读3分钟

对于看过的,如何整理出来,如何记住,如果说出,如何使用,如何读源码呢? 在开始的入口看一下,然后时对应的resource,config,filter,intercepter,listener这些看一下 先看一下大体的流程时做什么的?先不要看细节哦! 入口怎么找?回归最初的Spring xml配置的时候

mybatis-plus source code 解读

事务传播

7种,默认Propagation.REQUIRED

- REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED)
Spring默认事务传播方式,当处于事务中,新的开始事务时,加入该事务
- SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS)
有事务就加入事务,没有就无事务运行
- MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY)

- REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW)
有事务就新建事务
- NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED)
有事务就事务指向,没有就无事务执行
- NEVER(TransactionDefinition.PROPAGATION_NEVER)
无事务就指向,有事务报异常
- NESTED(TransactionDefinition.PROPAGATION_NESTED)
嵌套事务

隔离级别

5种,默认Default级别 MySQL的4种事务,加上Spring的默认Default级别

DEFAULT(TransactionDefinition.ISOLATION_DEFAULT)
Spring默认级别,分别对应不同数据库对应不同的默认级别
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
读未提交
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
读已提交
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
可重复读
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
串行化

数据库异常

1. 脏读
定义:读到另一个事务未提交的数据
读已提交可解决脏读问题

2. 不可重复读
定义:同一行数据,第一次读取出后,另一个线程修改了数据,再次读取该数据时,获取到的结果不一样
可重复读级别解决不可重复读问题

3. 幻读
定义:读取一批数据,第一次读取时是10条,另一个线程新增或删除了其中的数据,再次读取的时候,数据量变了
串行化可解决幻读问题

事务所需要的基础配置

org.springframework.transaction.annotation.SpringTransactionAnnotationParser.parseTransactionAnnotation(org.springframework.core.annotation.AnnotationAttributes)

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注解使用的是切面进行实现

Spring AOP切面

前置后置环绕异常return返回
增强(通知类型)
AOP切面实现方式
JDK动态代理(运行时增强)CGLIB代理(运行时增强)AspectJ代理(编译时增强)
动态代理使用
日志鉴权事务

术语:切面,切点,织入,引入,增强,连接点。

记忆:AOP通过在 【连接点】【引入】属性和方法,得到【增强】的对象 ---切点,切面,织入

如何查看源码? 从基础配置开始

 <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--控制住数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--开启基于注解的事务,使用xml形式配置的事务(必要主要的都是使用配置式)-->
    <!--可以使用注解@Transactional开启事务-->
    <tx:annotation-driven/>

1.找到事务管理类 2.找到解析 annotation-driven 的类(找到spring的tx包,然后,在META-INF中的spring.handlers中找到对应的TxNamespaceHandler类 spirng.handlers,spring.scheme配置增强的xml自定义标签 tx:annotation-driven/便是这样的增强标签

org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser.parse


    /**
    * Parses the {@code <tx:annotation-driven/>} tag. Will
    * {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
    * with the container as necessary.
    */
   @Override
   @Nullable
   public BeanDefinition parse(Element element, ParserContext parserContext) {
      registerTransactionalEventListenerFactory(parserContext);
      String mode = element.getAttribute("mode");
      if ("aspectj".equals(mode)) {
         // mode="aspectj"
         registerTransactionAspect(element, parserContext);
         if (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader())) {
            registerJtaTransactionAspect(element, parserContext);
         }
      }
      else {
          // 一般默认的时JDK动态代理
         // mode="proxy"
         AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
            /**
             * AopAutoProxyConfigurer 的工作
             * 1.创建 TransactionAttributeSource
             * 2.创建 TransactionInterceptor 进行具体的方法进行代理工作
             * 3.创建 TransactionAttributeSourceAdvisor 用于切点,织入
             * 4.嵌套在 CompositeComponentDefinition 将1.2.3创建的组件组合在一起,注册到context中
             * 5.注册到 ParserContext
             */
      }
      return null;
   }

Spring AOP 通过【连接点】【引入】属性和方法,【增强】代理类 切点,切面,织入

Spring AOP 的 org.springframework.aop.config.AopNamespaceHandler

Spring.handlers, Spring.schemas 自定义xml注解