手写Spring:第20章-事务处理

文章目录

  • 一、目标:事务处理
  • 二、设计:事务处理
    • 2.1 事务单元测试
    • 2.2 事务设计
  • 三、实现:事务处理
    • 3.1 工程结构
    • 3.2 事务管理的核心类图
    • 3.3 定义事务注解
      • 3.3.1 定义事务注解
      • 3.3.2 定义事务接口
      • 3.3.3 默认事务定义实现类
      • 3.3.4 委托事务定义实现类
    • 3.4 事务属性
      • 3.4.1 事务属性接口
      • 3.4.2 默认事务属性实现类
      • 3.4.3 委托事务属性实现类
      • 3.4.4 回滚规则属性
      • 3.4.5 基于规则的事务属性实现类
    • 3.5 注解工具包
      • 3.5.1 注解属性
      • 3.5.2 注解类型工具类
    • 3.6 解析事务注解
      • 3.6.1 用于解析已知事务注释类型的策略接口
      • 3.6.2 Spring的事务注释类型解析实现类
    • 3.7 事务状态
      • 3.7.1 事务执行接口
      • 3.7.2 存档管理接口
      • 3.7.3 事务状态接口
      • 3.7.4 事务状态抽象类
      • 3.7.5 默认事务状态
    • 3.8 事务属性来源
      • 3.8.1 事务属性来源接口
      • 3.8.2 事务属性来源抽象类
      • 3.8.3 注解事务属性来源实现类
    • 3.9 事务拦截处理
      • 3.9.1 事务管理器平台接口
      • 3.9.2 事务管理器平台抽象类
      • 3.9.3 事务处理
      • 3.9.4 事务拦截器
      • 3.9.5 事务同步管理器
    • 3.10 获取数据库连接
      • 3.10.1 数据库连接处理器接口
      • 3.10.2 简单数据库连接处理器
      • 3.10.3 数据库连接处理器
      • 3.10.4 JDBC事务支撑对象
      • 3.10.5 数据源事务管理器
      • 3.10.6 数据源操作抽象类
      • 3.10.7 JDBC操作模板
  • 四、测试:事务处理
    • 4.1 添加测试配置
      • 4.1.1 事务测试类
      • 4.1.2 Spring属性配置文件
      • 4.1.3 初始化测试
    • 4.2 单元测试
      • 4.2.1 有事务测试
      • 4.2.2 无事务测试
  • 五、总结:事务处理

一、目标:事务处理

如何运用 @Transaction 注解标识、AOP 切面包装、将 JDBC 中的事务操作功能整合到 Spring 的框架结构中?

  • 事务: 数据必须保证完整性和一致性。这也是事务的4个特性:ACID
    • 原子性(Atomicity):事务是一个原子操作,由一系列的动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
    • 一致性(Consistency):一旦事务完成(无论成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不是部分完成、部分失败。现实中的数据不应该被破坏。
    • 隔离性(Isolation):许多事务可能会同时处理相同的数据,因此每个事务都应该与其他事务隔离,防止数据被损坏。
    • 持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。在通常情况下,事务的结果会被写到持久化存储器中。

二、设计:事务处理

2.1 事务单元测试

JdbcTest.java

package com.lino.springframework.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.mysql.cj.jdbc.Driver;
import org.junit.Before;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @description: JDBC测试类
 */
public class JdbcTest {

    private DruidDataSource dataSource;
    private Connection connection;
    private Statement statement;

    @Before
    public void init() throws SQLException {
        dataSource = new DruidDataSource();
        dataSource.setDriver(new Driver());
        dataSource.setUrl("jdbc:mysql://localhost:3306/spring?useSSL=false&serverTimezone=Asia/Shanghai");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
    }

    @Test
    public void test_translation() throws SQLException {
        connection = dataSource.getConnection().getConnection();
        statement = connection.createStatement();

        connection.setAutoCommit(false);
        try {
            statement.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
            statement.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
        } catch (Exception e) {
            e.printStackTrace();
            connection.rollback();
        }
        connection.commit();
    }
}
  • 这就是使用 JDBC 直接操作提交事务的方式,首先设置自动提交 connection.setAutoCommit(false) 来关闭事务,然后在程序处理过程中进行手动提交事务和回滚事务的操作。

测试结果

信息: {dataSource-1} inited
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
	at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
	at com.lino.springframework.test.JdbcTest.test_translation(JdbcTest.java:40)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)

在这里插入图片描述

2.2 事务设计

设计:事务的核心逻辑处理该如何完成?

  • 简单来说,我们最终目的就是把数据库的事务操作用自定义注解和 AOP 切面进行管理,并扩充相应的事务特性。

手写Spring:第20章-事务处理_第1张图片

  • 有了这个结构,就可以把这部分操作数据库事务的功能迁移到 Spring 中,使用注解标记和 AOP 切面进行拦截处理。
  • 如果想要提交事务,则需要保证是一个链接下进行处理的。这是需要引入事务管理器进行处理,使用 ThreadLocal 进行保存。
  • 只有定义了事务注解,才能对需要处理的事务进行方法拦截。在拦截到事务之后,把这种操作方法交给代理类,也就是把用户对数据库的操作包装到事务的代码库中,方便对操作方法进行事务提交和回滚等操作。

三、实现:事务处理

3.1 工程结构

spring-step-19
|-src
	|-main
	|	|-java
	|		|-com.lino.springframework
	|			|-aop
	|			|	|-aspectj
	|			|	|	|-AspectJExpressionPointcut.java
	|			|	|	|-AspectJExpressionPointcutAdvisor.java
	|			|	|-framework
	|			|	|	|-adapter
	|			|	|	|	|-MethodBeforeAdviceInterceptor.java
	|			|	|	|-autoproxy
	|			|	|	|	|-DefaultAdvisorAutoProxyCreator.java
	|			|	|	|-AopProxy.java
	|			|	|	|-Cglib2AopProxy.java
	|			|	|	|-JdkDynamicAopProxy.java
	|			|	|	|-ProxyFactory.java
	|			|	|	|-ReflectiveMethodInvocation.java
	|			|	|-AdvisedSupport.java
	|			|	|-Advisor.java
	|			|	|-BeforeAdvice.java
	|			|	|-ClassFilter.java
	|			|	|-MethodBeforeAdvice.java
	|			|	|-MethodMatcher.java
	|			|	|-Pointcut.java
	|			|	|-PointcutAdvisor.java
	|			|	|-TargetSource.java
	|			|-beans
	|			|	|-factory
	|			|	|	|-annotation
	|			|	|	|	|-Autowired.java
	|			|	|	|	|-AutowiredAnnotationBeanPostProcessor.java
	|			|	|	|	|-Qualifier.java
	|			|	|	|	|-Value.java
	|			|	|	|-config
	|			|	|	|	|-AutowireCapableBeanFactory.java
	|			|	|	|	|-BeanDefinition.java
	|			|	|	|	|-BeanFactoryPostProcessor.java
	|			|	|	|	|-BeanPostProcessor.java
	|			|	|	|	|-BeanReference.java
	|			|	|	|	|-ConfigurableBeanFactory.java
	|			|	|	|	|-InstantiationAwareBeanPostProcessor.java
	|			|	|	|	|-SingletonBeanRegistry.java
	|			|	|	|-support
	|			|	|	|	|-AbstractAutowireCapableBeanFactory.java
	|			|	|	|	|-AbstractBeabDefinitionReader.java
	|			|	|	|	|-AbstractBeabFactory.java
	|			|	|	|	|-BeabDefinitionReader.java
	|			|	|	|	|-BeanDefinitionRegistry.java
	|			|	|	|	|-CglibSubclassingInstantiationStrategy.java
	|			|	|	|	|-DefaultListableBeanFactory.java
	|			|	|	|	|-DefaultSingletonBeanRegistry.java
	|			|	|	|	|-DisposableBeanAdapter.java
	|			|	|	|	|-FactoryBeanRegistrySupport.java
	|			|	|	|	|-InstantiationStrategy.java
	|			|	|	|	|-SimpleInstantiationStrategy.java
	|			|	|	|-xml
	|			|	|	|	|-XmlBeanDefinitionReader.java
	|			|	|	|-Aware.java
	|			|	|	|-BeanClassLoaderAware.java
	|			|	|	|-BeanFactory.java
	|			|	|	|-BeanFactoryAware.java
	|			|	|	|-BeanNameAware.java
	|			|	|	|-ConfigurableListableBeanFactory.java
	|			|	|	|-DisposableBean.java
	|			|	|	|-FactoryBean.java
	|			|	|	|-HierarcgicalBeanFactory.java
	|			|	|	|-InitializingBean.java
	|			|	|	|-ListableBeanFactory.java
	|			|	|	|-ObjectFactory.java
	|			|	|	|-PropertyPlaceholderConfigurer.java
	|			|	|-BeansException.java
	|			|	|-PropertyValue.java
	|			|	|-PropertyValues.java
	|			|-context
	|			|	|-annotation
	|			|	|	|-ClassPathBeanDefinitionScanner.java
	|			|	|	|-ClassPathScanningCandidateComponentProvider.java
	|			|	|	|-Scope.java
	|			|	|-event
	|			|	|	|-AbstractApplicationEventMulticaster.java
	|			|	|	|-ApplicationContextEvent.java
	|			|	|	|-ApplicationEventMulticaster.java
	|			|	|	|-ContextclosedEvent.java
	|			|	|	|-ContextRefreshedEvent.java
	|			|	|	|-SimpleApplicationEventMulticaster.java
	|			|	|-support
	|			|	|	|-AbstractApplicationContext.java
	|			|	|	|-AbstractRefreshableApplicationContext.java
	|			|	|	|-AbstractXmlApplicationContext.java
	|			|	|	|-ApplicationContextAwareProcessor.java
	|			|	|	|-ClassPathXmlApplicationContext.java
	|			|	|	|-ConversionServiceFactoryBean.java
	|			|	|-ApplicationContext.java
	|			|	|-ApplicationContextAware.java
	|			|	|-ApplicationEvent.java
	|			|	|-ApplicationEventPublisher.java
	|			|	|-ApplicationListener.java
	|			|	|-ConfigurableApplicationContext.java
	|			|-core
	|			|	|-annotation
	|			|	|	|-AbstractAliasAwareAnnotationAttributeExtractor.java
	|			|	|	|-AliasFor.java
	|			|	|	|-AnnotatedElementUtils.java
	|			|	|	|-AnnotationAttributeExtractor.java
	|			|	|	|-AnnotationAttributes.java
	|			|	|	|-AnnotationConfigurationException.java
	|			|	|	|-AnnotationUtils.java
	|			|	|	|-DefaultAnnotationAttributeExtractor.java
	|			|	|	|-SynthesizedAnnotation.java
	|			|	|	|-SynthesizedAnnotationInvocationHandler.java
	|			|	|-convert
	|			|	|	|-converter
	|			|	|	|	|-Converter.java
	|			|	|	|	|-ConverterFactory.java
	|			|	|	|	|-ConverterRegistry.java
	|			|	|	|	|-GenericConverter.java
	|			|	|	|-support
	|			|	|	|	|-DefaultConversionService.java
	|			|	|	|	|-GenericConversionService.java
	|			|	|	|	|-StringToNumberConverterFactory.java
	|			|	|	|-ConversionService
	|			|	|-io
	|			|	|	|-ClassPathResource.java
	|			|	|	|-DefaultResourceLoader.java
	|			|	|	|-FileSystemResource.java
	|			|	|	|-Resource.java
	|			|	|	|-ResourceLoader.java
	|			|	|	|-UrlResource.java
	|			|	|-util
	|			|	|	|-ConcurrentReferenceHashMap.java
	|			|	|	|-ObjectUtils.java
	|			|	|	|-StringUtils.java
	|			|	|-BridgeMethodResolver.java
	|			|	|-GraalDetector.java
	|			|	|-MethodClassKey.java
	|			|	|-MethodParameter.java
	|			|	|-ResolvableType.java
	|			|	|-SerializableTypeWrapper.java
	|			|-jdbc
	|			|	|-core
	|			|	|	|-ColumnMapRowMapper.java
	|			|	|	|-JdbcOperations.java
	|			|	|	|-JdbcTemplate.java
	|			|	|	|-ResultSetExtractor.java
	|			|	|	|-RowMapper.java
	|			|	|	|-RowMapperResultSetExtractor.java
	|			|	|	|-SqlProvider.java
	|			|	|	|-StatementCallback.java
	|			|	|-datasource
	|			|	|	|-ConnectionHandle.java
	|			|	|	|-ConnectionHolder.java
	|			|	|	|-DataSourceTransactionManager.java
	|			|	|	|-DataSourceUtils.java
	|			|	|	|-JdbcTransactionObjectSupport.java
	|			|	|	|-SimpleConnectionHandle.java
	|			|	|-support
	|			|	|	|-JdbcAccessor.java
	|			|	|	|-JdbcUtils.java
	|			|	|-CannotGetJdbcConnectionException.java
	|			|	|-UncategorizedSQLException.java
	|			|-stereotype
	|			|	|-Component.java
	|			|-tx.transaction
	|			|	|-annotation
	|			|	|	|-AnnotationTransactionAttributeSource.java
	|			|	|	|-SpringTransactionAnnotationParser.java
	|			|	|	|-Transactional.java
	|			|	|	|-TransactionAnnotationParser.java
	|			|	|-interceptor
	|			|	|	|-AbstractFallbackTransactionAttributeSource.java
	|			|	|	|-DefaultTransactionAttribute.java
	|			|	|	|-DelegatingTransactionAttribute.java
	|			|	|	|-RollbackRuleAttribute.java
	|			|	|	|-RuleBasedTransactionAttribute.java
	|			|	|	|-TransactionAspectSupport.java
	|			|	|	|-TransactionAttribute.java
	|			|	|	|-TransactionAttributeSource.java
	|			|	|	|-TransactionInterceptor.java
	|			|	|-support
	|			|	|	|-AbstractPlatformTransactionManager.java
	|			|	|	|-AbstractTransactionStatus.java
	|			|	|	|-DefaultTransactionDefinition.java
	|			|	|	|-DefaultTransactionStatus.java
	|			|	|	|-DelegatingTransactionDefinition.java
	|			|	|	|-TransactionSynchronizationManager.java
	|			|	|-CannotCreateTransactionException.java
	|			|	|-NestedTransactionNotSupportedException.java
	|			|	|-PlatformTransactionManager.java
	|			|	|-SavepointManager.java
	|			|	|-TransactionDefinition.java
	|			|	|-TransactionException.java
	|			|	|-TransactionExecution.java
	|			|	|-TransactionStatus.java
	|			|-util
	|			|	|-ClassUtils.java
	|			|	|-NumberUtils.java
	|			|	|-ReflectionUtils.java
	|			|	|-StringValueResolver.java
	|-test
		|-java
			|-com.lino.springframework.test
				|-bean
					|-JdbcService.java
                |-ApiTest.java
                |-JdbcTest.java
		|-resources
			|-spring.xml

3.2 事务管理的核心类图

手写Spring:第20章-事务处理_第2张图片

  • 以自定义事务注解 @Transactional 进行方法标记,用于事务管理扫描。把需要被事务管理的方法拦截 AOP 切面。
  • 因为事务需要在同一个链接下操作,所以需要使用 TransactionSynchronizationManager 类提供的 ThreadLocal 方法记录当前线程下的链接信息。
  • 事务的控制都需要被切面事务支撑类 TransactionAspectSupport 进行包装和处理,以便提交和回滚事务。

3.3 定义事务注解

3.3.1 定义事务注解

Transactional.java

package com.lino.springframework.tx.transaction.annotation;

import java.lang.annotation.*;

/**
 * @description: 事务注解
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

    /**
     * 回滚
     */
    Class<? extends Throwable>[] rollbackFor() default {};
}
  • 只有作用到方法上的事务标记性注解,才能被 SpringTransactionAnnotationParser 类扫描并管理事务。

3.3.2 定义事务接口

TransactionDefinition.java

package com.lino.springframework.tx.transaction;

import java.sql.Connection;

/**
 * @description: 事务定义接口
 */
public interface TransactionDefinition {

    /**
     * 这个是Spring默认的事务传播行为
     * 如果正处于一个事务中,则加入,否则创建一个新的事务
     */
    int PROPAGATION_REQUIRED = 0;

    /**
     * 如果正处于一个事务中,则加入,否则不使用事务
     */
    int PROPAGATION_SUPPORTS = 1;

    /**
     * 如果正处于一个事务中,则加入,否则抛出事务
     */
    int PROPAGATION_MANDATORY = 2;

    /**
     * 无论如何都会创建一个新事务
     * 如果正处于一个事务中,则先挂起当前事务,然后创建
     */
    int PROPAGATION_REQUIRES_NEW = 3;

    /**
     * 不使用事务
     * 如果正处于一个事务中,则挂起当前事务,不使用
     */
    int PROPAGATION_NOT_SUPPORTED = 4;

    /**
     * 不使用事务
     * 如果正处于一个事务中,则抛出异常
     */
    int PROPAGATION_NEVER = 5;

    /**
     * 嵌套事务
     * 如果正处于一个事务中,则创建一个事务嵌套其中,否则创建一个新事务
     */
    int PROPAGATION_NESTED = 6;

    /**
     * 使用默认的隔离级别
     */
    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;

    /**
     * 默认超时时间
     */
    int TIMEOUT_DEFAULT = -1;

    /**
     * 获取传播行为
     */
    int getPropagationBehavior();

    /**
     * 获取事务隔离级别
     */
    int getIsolationLevel();

    /**
     * 获取事务超时时间
     */
    int getTimeout();

    /**
     * 是否总是读
     */
    boolean isReadOnly();

    /**
     * 获取事务名称
     */
    String getName();
}

3.3.3 默认事务定义实现类

DefaultTransactionDefinition.java

package com.lino.springframework.tx.transaction.support;

import com.lino.springframework.tx.transaction.TransactionDefinition;
import java.io.Serializable;

/**
 * @description: 默认事务定义
 */
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {

    private int propagationBehavior = PROPAGATION_REQUIRED;

    private int isolationLevel = ISOLATION_DEFAULT;

    private int timeout = TIMEOUT_DEFAULT;

    private boolean readOnly = false;

    private String name;

    public DefaultTransactionDefinition() {
    }

    public DefaultTransactionDefinition(TransactionDefinition other) {
        this.propagationBehavior = other.getPropagationBehavior();
        this.isolationLevel = other.getIsolationLevel();
        this.timeout = other.getTimeout();
        this.readOnly = other.isReadOnly();
        this.name = other.getName();
    }

    public DefaultTransactionDefinition(int propagationBehavior) {
        this.propagationBehavior = propagationBehavior;
    }

    public void setPropagationBehavior(int propagationBehavior) {
        this.propagationBehavior = propagationBehavior;
    }

    @Override
    public int getPropagationBehavior() {
        return this.propagationBehavior;
    }

    public void setIsolationLevel(int isolationLevel) {
        this.isolationLevel = isolationLevel;
    }

    @Override
    public int getIsolationLevel() {
        return this.isolationLevel;
    }

    public void setTimeout(int timeout) {
        if (timeout < TIMEOUT_DEFAULT) {
            throw new IllegalArgumentException("Timeout must be a positive integer or TIMEOUT_DEFAULT");
        }
        this.timeout = timeout;
    }

    @Override
    public int getTimeout() {
        return this.timeout;
    }

    public void setReadOnly(boolean readOnly) {
        this.readOnly = readOnly;
    }

    @Override
    public boolean isReadOnly() {
        return this.readOnly;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

3.3.4 委托事务定义实现类

DelegatingTransactionDefinition.java

package com.lino.springframework.tx.transaction.support;

import com.lino.springframework.tx.transaction.TransactionDefinition;
import java.io.Serializable;
import java.util.Objects;

/**
 * @description: 委托事务定义
 */
public class DelegatingTransactionDefinition implements TransactionDefinition, Serializable {

    private final TransactionDefinition targetDefinition;

    public DelegatingTransactionDefinition(TransactionDefinition targetDefinition) {
        this.targetDefinition = targetDefinition;
    }

    @Override
    public int getPropagationBehavior() {
        return this.targetDefinition.getPropagationBehavior();
    }

    @Override
    public int getIsolationLevel() {
        return this.targetDefinition.getIsolationLevel();
    }

    @Override
    public int getTimeout() {
        return this.targetDefinition.getTimeout();
    }

    @Override
    public boolean isReadOnly() {
        return this.targetDefinition.isReadOnly();
    }

    @Override
    public String getName() {
        return this.targetDefinition.getName();
    }

    @Override
    public boolean equals(Object other) {
        return this.targetDefinition.equals(other);
    }

    @Override
    public int hashCode() {
        return this.targetDefinition.hashCode();
    }

    @Override
    public String toString() {
        return this.targetDefinition.toString();
    }
}

3.4 事务属性

3.4.1 事务属性接口

TransactionAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.tx.transaction.TransactionDefinition;

/**
 * @description: 事务属性
 */
public interface TransactionAttribute extends TransactionDefinition {

    /**
     * 回滚
     *
     * @param ex 异常
     * @return 回滚结果
     */
    boolean rollbackOn(Throwable ex);
}

3.4.2 默认事务属性实现类

DefaultTransactionAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.tx.transaction.support.DefaultTransactionDefinition;

/**
 * @description: 默认事务属性
 */
public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {

    public DefaultTransactionAttribute() {
        super();
    }

    @Override
    public boolean rollbackOn(Throwable ex) {
        return (ex instanceof RuntimeException || ex instanceof Error);
    }

    @Override
    public String toString() {
        return "DefaultTransactionAttribute{}";
    }

}

3.4.3 委托事务属性实现类

DelegatingTransactionAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.tx.transaction.support.DelegatingTransactionDefinition;
import java.io.Serializable;

/**
 * @description: 委托事务属性
 */
public class DelegatingTransactionAttribute extends DelegatingTransactionDefinition implements TransactionAttribute, Serializable {

    private final TransactionAttribute targetAttribute;

    public DelegatingTransactionAttribute(TransactionAttribute targetAttribute) {
        super(targetAttribute);
        this.targetAttribute = targetAttribute;
    }

    @Override
    public boolean rollbackOn(Throwable ex) {
        return this.targetAttribute.rollbackOn(ex);
    }
}

3.4.4 回滚规则属性

RollbackRuleAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import java.io.Serializable;

/**
 * @description: 回滚规则属性
 */
public class RollbackRuleAttribute implements Serializable {

    private final String exceptionName;

    public RollbackRuleAttribute(Class<?> clazz) {
        this.exceptionName = clazz.getName();
    }
}

3.4.5 基于规则的事务属性实现类

RuleBasedTransactionAttribute.java

package com.lino.springframework.tx.transaction.interceptor;

import java.io.Serializable;
import java.util.List;

/**
 * @description: 基于规则的事务属性
 */
public class RuleBasedTransactionAttribute extends DefaultTransactionAttribute implements Serializable {

    private List<RollbackRuleAttribute> rollbackRules;

    public RuleBasedTransactionAttribute() {
        super();
    }

    public void setRollbackRules(List<RollbackRuleAttribute> rollbackRules) {
        this.rollbackRules = rollbackRules;
    }
}

3.5 注解工具包

3.5.1 注解属性

AnnotationAttributes.java

package com.lino.springframework.core.annotation;

import cn.hutool.core.lang.Assert;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.LinkedHashMap;

/**
 * @description: 注解属性
 */
public class AnnotationAttributes extends LinkedHashMap<String, Object> {

    private final Class<? extends Annotation> annotationType;

    boolean validated = false;

    final String displayName;

    public AnnotationAttributes(Class<? extends Annotation> annotationType) {
        Assert.notNull(annotationType, "'annotationType' must not be null");
        this.annotationType = annotationType;
        this.displayName = annotationType.getName();
    }

    public Class<?>[] getClassArray(String attributeName) {
        return getRequiredAttribute(attributeName, Class[].class);
    }

    private <T> T getRequiredAttribute(String attributeName, Class<T> expectedType) {
        Object value = get(attributeName);

        if (!expectedType.isInstance(value) && expectedType.isArray() && expectedType.getComponentType().isInstance(value)) {
            Object array = Array.newInstance(expectedType.getComponentType(), 1);
            Array.set(array, 0, value);
            value = array;
        }

        return (T) value;
    }

    public Class<? extends Annotation> annotationType() {
        return this.annotationType;
    }
}

3.5.2 注解类型工具类

AnnotatedElementUtils.java

package com.lino.springframework.core.annotation;

import cn.hutool.core.collection.CollUtil;
import com.lino.springframework.core.BridgeMethodResolver;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @description: 注解类型工具类
 */
public class AnnotatedElementUtils {

    private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];

    public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
                                                                      Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
        return searchWithFindSemantics(element, annotationType, null, new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
    }

    private static <T> T searchWithFindSemantics(AnnotatedElement element,
                                                 Class<? extends Annotation> annotationType,
                                                 String annotationName, Processor<T> processor) {
        return searchWithFindSemantics(element, (null != annotationType ? Collections.singleton(annotationType) : Collections.emptySet()),
                annotationName, null, processor);
    }

    private static <T> T searchWithFindSemantics(AnnotatedElement element,
                                                 Set<Class<? extends Annotation>> annotationType,
                                                 String annotationName,
                                                 Class<? extends Annotation> containerType,
                                                 Processor<T> processor) {
        if (containerType != null && !processor.aggregates()) {
            throw new IllegalArgumentException("Search for repeatable annotations must supply an aggregating Processor");
        }

        return searchWithFindSemantics(element, annotationType, annotationName, containerType, processor, new HashSet<>(), 0);

    }

    private static <T> T searchWithFindSemantics(AnnotatedElement element,
                                                 Set<Class<? extends Annotation>> annotationTypes, String annotationName,
                                                 Class<? extends Annotation> containerType, Processor<T> processor,
                                                 Set<AnnotatedElement> visited, int metaDepth) {
        if (visited.add(element)) {
            try {
                // Locally declared annotations (ignoring @Inherited)
                Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(element);
                if (annotations.length > 0) {
                    List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);

                    // Search in local annotations
                    for (Annotation annotation : annotations) {
                        Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
                        if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
                            if (annotationTypes.contains(currentAnnotationType) ||
                                    currentAnnotationType.getName().equals(annotationName) ||
                                    processor.alwaysProcesses()) {
                                T result = processor.process(element, annotation, metaDepth);
                                if (result != null) {
                                    if (aggregatedResults != null && metaDepth == 0) {
                                        aggregatedResults.add(result);
                                    } else {
                                        return result;
                                    }
                                }
                            }
                            // Repeatable annotations in container?
                            else if (currentAnnotationType == containerType) {
                                for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
                                    T result = processor.process(element, contained, metaDepth);
                                    if (aggregatedResults != null && result != null) {
                                        // No need to post-process since repeatable annotations within a
                                        // container cannot be composed annotations.
                                        aggregatedResults.add(result);
                                    }
                                }
                            }
                        }
                    }

                    // Recursively search in meta-annotations
                    for (Annotation annotation : annotations) {
                        Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
                        if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
                            T result = searchWithFindSemantics(currentAnnotationType, annotationTypes, annotationName,
                                    containerType, processor, visited, metaDepth + 1);
                            if (result != null) {
                                processor.postProcess(currentAnnotationType, annotation, result);
                                if (aggregatedResults != null && metaDepth == 0) {
                                    aggregatedResults.add(result);
                                } else {
                                    return result;
                                }
                            }
                        }
                    }

                    if (!CollUtil.isEmpty(aggregatedResults)) {
                        // Prepend to support top-down ordering within class hierarchies
                        processor.getAggregatedResults().addAll(0, aggregatedResults);
                    }
                }

                if (element instanceof Method) {
                    Method method = (Method) element;
                    T result;

                    // Search on possibly bridged method
                    Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
                    if (resolvedMethod != method) {
                        result = searchWithFindSemantics(resolvedMethod, annotationTypes, annotationName,
                                containerType, processor, visited, metaDepth);
                        if (result != null) {
                            return result;
                        }
                    }

                    // Search on methods in interfaces declared locally
                    Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
                    if (ifcs.length > 0) {
                        result = searchOnInterfaces(method, annotationTypes, annotationName,
                                containerType, processor, visited, metaDepth, ifcs);
                        if (result != null) {
                            return result;
                        }
                    }

                    // Search on methods in class hierarchy and interface hierarchy
                    Class<?> clazz = method.getDeclaringClass();
                    while (true) {
                        clazz = clazz.getSuperclass();
                        if (clazz == null || clazz == Object.class) {
                            break;
                        }
                        Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
                        if (!annotatedMethods.isEmpty()) {
                            for (Method annotatedMethod : annotatedMethods) {
                                if (AnnotationUtils.isOverride(method, annotatedMethod)) {
                                    Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod);
                                    result = searchWithFindSemantics(resolvedSuperMethod, annotationTypes,
                                            annotationName, containerType, processor, visited, metaDepth);
                                    if (result != null) {
                                        return result;
                                    }
                                }
                            }
                        }
                        // Search on interfaces declared on superclass
                        result = searchOnInterfaces(method, annotationTypes, annotationName,
                                containerType, processor, visited, metaDepth, clazz.getInterfaces());
                        if (result != null) {
                            return result;
                        }
                    }
                } else if (element instanceof Class) {
                    Class<?> clazz = (Class<?>) element;
                    if (!Annotation.class.isAssignableFrom(clazz)) {
                        // Search on interfaces
                        for (Class<?> ifc : clazz.getInterfaces()) {
                            T result = searchWithFindSemantics(ifc, annotationTypes, annotationName,
                                    containerType, processor, visited, metaDepth);
                            if (result != null) {
                                return result;
                            }
                        }
                        // Search on superclass
                        Class<?> superclass = clazz.getSuperclass();
                        if (superclass != null && superclass != Object.class) {
                            T result = searchWithFindSemantics(superclass, annotationTypes, annotationName,
                                    containerType, processor, visited, metaDepth);
                            if (result != null) {
                                return result;
                            }
                        }
                    }
                }
            } catch (Throwable ex) {
                AnnotationUtils.handleIntrospectionFailure(element, ex);
            }
        }
        return null;
    }

    private static <T> T searchOnInterfaces(Method method, Set<Class<? extends Annotation>> annotationTypes,
                                            String annotationName, Class<? extends Annotation> containerType,
                                            Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {

        for (Class<?> ifc : ifcs) {
            Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(ifc);
            if (!annotatedMethods.isEmpty()) {
                for (Method annotatedMethod : annotatedMethods) {
                    if (AnnotationUtils.isOverride(method, annotatedMethod)) {
                        T result = searchWithFindSemantics(annotatedMethod, annotationTypes, annotationName,
                                containerType, processor, visited, metaDepth);
                        if (result != null) {
                            return result;
                        }
                    }
                }
            }
        }

        return null;
    }

    private static <A extends Annotation> A[] getRawAnnotationsFromContainer(AnnotatedElement element, Annotation container) {

        try {
            A[] value = (A[]) AnnotationUtils.getValue(container);
            if (value != null) {
                return value;
            }
        } catch (Throwable ex) {
            AnnotationUtils.handleIntrospectionFailure(element, ex);
        }
        // Unable to read value from repeating annotation container -> ignore it.
        return (A[]) EMPTY_ANNOTATION_ARRAY;
    }

    public static AnnotatedElement forAnnotations(final Annotation... annotations) {

        return new AnnotatedElement() {
            @Override
            public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
                for (Annotation annotation : annotations) {
                    if (annotation.annotationType() == annotationClass) {
                        return (T) annotation;
                    }
                }
                return null;
            }

            @Override
            public Annotation[] getAnnotations() {
                return annotations;
            }

            @Override
            public Annotation[] getDeclaredAnnotations() {
                return annotations;
            }
        };
    }

    private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> {

        private final boolean classValuesAsString;

        private final boolean nestedAnnotationsAsMap;

        private final boolean aggregates;

        private final List<AnnotationAttributes> aggregatedResults;

        MergedAnnotationAttributesProcessor() {
            this(false, false, false);
        }

        MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
            this(classValuesAsString, nestedAnnotationsAsMap, false);
        }

        MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap,
                                            boolean aggregates) {

            this.classValuesAsString = classValuesAsString;
            this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
            this.aggregates = aggregates;
            this.aggregatedResults = (aggregates ? new ArrayList<>() : Collections.emptyList());
        }

        @Override
        public boolean alwaysProcesses() {
            return false;
        }

        @Override
        public boolean aggregates() {
            return this.aggregates;
        }

        @Override
        public List<AnnotationAttributes> getAggregatedResults() {
            return this.aggregatedResults;
        }

        @Override
        public AnnotationAttributes process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
            return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,
                    this.classValuesAsString, this.nestedAnnotationsAsMap);
        }

        @Override
        public void postProcess(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) {
            annotation = AnnotationUtils.synthesizeAnnotation(annotation, element);
            Class<? extends Annotation> targetAnnotationType = attributes.annotationType();

            // Track which attribute values have already been replaced so that we can short
            // circuit the search algorithms.
            Set<String> valuesAlreadyReplaced = new HashSet<>();

            for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
                String attributeName = attributeMethod.getName();
                String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);

                // Explicit annotation attribute override declared via @AliasFor
                if (attributeOverrideName != null) {
                    if (valuesAlreadyReplaced.contains(attributeOverrideName)) {
                        continue;
                    }

                    List<String> targetAttributeNames = new ArrayList<>();
                    targetAttributeNames.add(attributeOverrideName);
                    valuesAlreadyReplaced.add(attributeOverrideName);

                    // Ensure all aliased attributes in the target annotation are overridden. (SPR-14069)
                    List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName);
                    if (aliases != null) {
                        for (String alias : aliases) {
                            if (!valuesAlreadyReplaced.contains(alias)) {
                                targetAttributeNames.add(alias);
                                valuesAlreadyReplaced.add(alias);
                            }
                        }
                    }

                    overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames);
                }
                // Implicit annotation attribute override based on convention
                else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
                    overrideAttribute(element, annotation, attributes, attributeName, attributeName);
                }
            }
        }

        private void overrideAttributes(AnnotatedElement element, Annotation annotation,
                                        AnnotationAttributes attributes, String sourceAttributeName, List<String> targetAttributeNames) {

            Object adaptedValue = getAdaptedValue(element, annotation, sourceAttributeName);

            for (String targetAttributeName : targetAttributeNames) {
                attributes.put(targetAttributeName, adaptedValue);
            }
        }

        private void overrideAttribute(AnnotatedElement element, Annotation annotation,
                                       AnnotationAttributes attributes, String sourceAttributeName, String targetAttributeName) {

            attributes.put(targetAttributeName, getAdaptedValue(element, annotation, sourceAttributeName));
        }

        private Object getAdaptedValue(
                AnnotatedElement element, Annotation annotation, String sourceAttributeName) {

            Object value = AnnotationUtils.getValue(annotation, sourceAttributeName);
            return AnnotationUtils.adaptValue(element, value, this.classValuesAsString, this.nestedAnnotationsAsMap);
        }
    }

    private interface Processor<T> {

        T process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth);

        void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result);

        boolean alwaysProcesses();

        boolean aggregates();

        List<T> getAggregatedResults();
    }
}
  • AnnotatedElementUtils 工具包源码包在 springframework.core 包下面,具体代码请查看源码。

3.6 解析事务注解

3.6.1 用于解析已知事务注释类型的策略接口

TransactionAnnotationParser.java

package com.lino.springframework.tx.transaction.annotation;

import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.lang.reflect.AnnotatedElement;

/**
 * @description: 用于解析已知事务注释类型的策略接口
 */
public interface TransactionAnnotationParser {

    /**
     * 基于该解析器理解的注释类型,解析给定方法或类的事务属性
     *
     * @param element 注释类型
     * @return 事务属性
     */
    TransactionAttribute parseTransactionAnnotation(AnnotatedElement element);
}

3.6.2 Spring的事务注释类型解析实现类

SpringTransactionAnnotationParser.java

package com.lino.springframework.tx.transaction.annotation;

import com.lino.springframework.core.annotation.AnnotatedElementUtils;
import com.lino.springframework.core.annotation.AnnotationAttributes;
import com.lino.springframework.tx.transaction.interceptor.RollbackRuleAttribute;
import com.lino.springframework.tx.transaction.interceptor.RuleBasedTransactionAttribute;
import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;

/**
 * @description: Spring的事务注释类型解析实现类
 */
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {

    @Override
    public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
        AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
                element, Transactional.class, false, false);
        if (null != attributes) {
            return parseTransactionAnnotation(attributes);
        } else {
            return null;
        }
    }

    protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
        RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();

        List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
        for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
            rollbackRules.add(new RollbackRuleAttribute(rbRule));
        }
        rbta.setRollbackRules(rollbackRules);
        return rbta;
    }
}
  • SpringTransactionAnnotationParser#parseTransactionAnnotation 方法用于提取配置注解 @Transactional 的方法。
    • 这里的提取操作使用了 AnnotatedElementUtils.findMergedAnnotationAttributes 方法,此方法来自 Spring Core 核心包提供的功能。
  • 接下来调用内部方法 parseTransactionAnnotation(AnnotationAttributes attributes),用于解析 @Transactional,并设置相关的事务属性。

3.7 事务状态

3.7.1 事务执行接口

TransactionExecution.java

package com.lino.springframework.tx.transaction;

/**
 * @description: 事务执行接口
 */
public interface TransactionExecution {

    /**
     * Return whether the present transaction is new; otherwise participating
     * in an existing transaction, or potentially not running in an actual
     * transaction in the first place.
     */
    boolean isNewTransaction();

    /**
     * Set the transaction rollback-only. This instructs the transaction manager
     * that the only possible outcome of the transaction may be a rollback, as
     * alternative to throwing an exception which would in turn trigger a rollback.
     */
    void setRollbackOnly();

    /**
     * Return whether the transaction has been marked as rollback-only
     * (either by the application or by the transaction infrastructure).
     */
    boolean isRollbackOnly();

    /**
     * Return whether this transaction is completed, that is,
     * whether it has already been committed or rolled back.
     */
    boolean isCompleted();
}

3.7.2 存档管理接口

SavepointManager.java

package com.lino.springframework.tx.transaction;

/**
 * @description: 存档管理接口
 */
public interface SavepointManager {

    /**
     * 保存存档
     */
    Object createSavepoint() throws TransactionException;

    /**
     * 回滚存档
     */
    void rollbackToSavepoint(Object savepoint) throws TransactionException;

    /**
     * 释放存档
     */
    void releaseSavepoint(Object savepoint) throws TransactionException;
}

3.7.3 事务状态接口

TransactionStatus.java

package com.lino.springframework.tx.transaction;

import java.io.Flushable;
import java.io.IOException;

/**
 * @description: 事务状态接口
 */
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {

    /**
     * 判断是否有保存点
     *
     * @return 判断是否有保存点
     */
    boolean hasSavepoint();

    /**
     * 刷新
     *
     * @throws IOException IO异常
     */
    @Override
    void flush() throws IOException;
}

3.7.4 事务状态抽象类

AbstractTransactionStatus.java

package com.lino.springframework.tx.transaction.support;

import com.lino.springframework.tx.transaction.NestedTransactionNotSupportedException;
import com.lino.springframework.tx.transaction.SavepointManager;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.TransactionStatus;
import java.io.IOException;

/**
 * @description: 事务状态抽象类
 */
public abstract class AbstractTransactionStatus implements TransactionStatus {

    private boolean rollbackOnly = false;

    private boolean completed = false;

    private Object savepoint;

    @Override
    public boolean isNewTransaction() {
        return false;
    }

    @Override
    public void setRollbackOnly() {
        this.rollbackOnly = true;
    }

    @Override
    public boolean isRollbackOnly() {
        return isLocalRollbackOnly() || isGlobalRollbackOnly();
    }

    public boolean isLocalRollbackOnly() {
        return this.rollbackOnly;
    }

    public boolean isGlobalRollbackOnly() {
        return false;
    }

    @Override
    public void flush() throws IOException {

    }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }

    @Override
    public boolean isCompleted() {
        return completed;
    }

    public Object getSavepoint() {
        return savepoint;
    }

    public void setSavepoint(Object savepoint) {
        this.savepoint = savepoint;
    }

    @Override
    public boolean hasSavepoint() {
        return this.savepoint != null;
    }

    @Override
    public Object createSavepoint() throws TransactionException {
        return getSavepointManager().createSavepoint();
    }

    @Override
    public void rollbackToSavepoint(Object savepoint) throws TransactionException {
        getSavepointManager().rollbackToSavepoint(savepoint);
    }

    @Override
    public void releaseSavepoint(Object savepoint) throws TransactionException {
        getSavepointManager().releaseSavepoint(savepoint);
    }

    protected SavepointManager getSavepointManager() {
        throw new NestedTransactionNotSupportedException("This transaction does not support savepoints");
    }
}

3.7.5 默认事务状态

DefaultTransactionStatus.java

package com.lino.springframework.tx.transaction.support;

/**
 * @description: 默认事务状态
 */
public class DefaultTransactionStatus extends AbstractTransactionStatus {

    private final Object transaction;

    private final boolean newTransaction;

    public DefaultTransactionStatus(Object transaction, boolean newTransaction) {
        this.transaction = transaction;
        this.newTransaction = newTransaction;
    }

    public Object getTransaction() {
        return transaction;
    }

    public boolean hasTransaction() {
        return this.transaction != null;
    }

    @Override
    public boolean isNewTransaction() {
        return hasTransaction() && this.newTransaction;
    }
}

3.8 事务属性来源

3.8.1 事务属性来源接口

TransactionAttributeSource.java

package com.lino.springframework.tx.transaction.interceptor;

import java.lang.reflect.Method;

/**
 * @description: 事务属性来源接口
 */
public interface TransactionAttributeSource {

    /**
     * 获取事务属性
     *
     * @param method      方法
     * @param targetClass 类类型
     * @return 事务属性
     */
    TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass);
}

3.8.2 事务属性来源抽象类

AbstractFallbackTransactionAttributeSource.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.core.MethodClassKey;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description: 事务属性来源接口抽象类
 */
public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {

    private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);

    private static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() {
        @Override
        public String toString() {
            return "null";
        }
    };

    @Override
    public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
        if (method.getDeclaringClass() == Object.class) {
            return null;
        }
        Object cacheKey = getCacheKey(method, targetClass);
        TransactionAttribute cached = this.attributeCache.get(cacheKey);
        if (null != cached) {
            if (cached == NULL_TRANSACTION_ATTRIBUTE) {
                return null;
            } else {
                return cached;
            }
        } else {
            TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
            if (null == txAttr) {
                this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
            } else {
                this.attributeCache.put(cacheKey, txAttr);
            }
            return txAttr;
        }
    }

    protected Object getCacheKey(Method method, Class<?> targetClass) {
        return new MethodClassKey(method, targetClass);
    }

    protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
        if (!Modifier.isPublic(method.getModifiers())) {
            return null;
        }
        TransactionAttribute txAttr = findTransactionAttribute(method);
        if (null != txAttr) {
            return txAttr;
        }
        return findTransactionAttribute(method.getDeclaringClass());
    }

    /**
     * 在方法上查找事务的相关属性
     *
     * @param method 方法
     * @return 事务属性
     */
    protected abstract TransactionAttribute findTransactionAttribute(Method method);

    /**
     * 在类上查找事务的相关属性
     *
     * @param clazz 类
     * @return 事务属性
     */
    protected abstract TransactionAttribute findTransactionAttribute(Class<?> clazz);
}

3.8.3 注解事务属性来源实现类

AnnotationTransactionAttributeSource.java

package com.lino.springframework.tx.transaction.annotation;

import com.lino.springframework.tx.transaction.interceptor.AbstractFallbackTransactionAttributeSource;
import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;

/**
 * @description: 注解事务属性来源接口抽象类
 */
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {

    private final boolean publicMethodsOnly;

    private final Set<TransactionAnnotationParser> annotationParsers;

    public AnnotationTransactionAttributeSource() {
        this(true);
    }

    public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
        this.publicMethodsOnly = publicMethodsOnly;
        this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
    }

    @Override
    protected TransactionAttribute findTransactionAttribute(Method method) {
        return determineTransactionAttribute(method);
    }

    @Override
    public TransactionAttribute findTransactionAttribute(Class<?> clazz) {
        return determineTransactionAttribute(clazz);
    }

    protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
        for (TransactionAnnotationParser annotationParser : annotationParsers) {
            TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
            if (attr != null) {
                return attr;
            }
        }
        return null;
    }
}

3.9 事务拦截处理

3.9.1 事务管理器平台接口

PlatformTransactionManager.java

package com.lino.springframework.tx.transaction;

/**
 * @description: 事务管理器平台
 */
public interface PlatformTransactionManager {

    /**
     * 获取事务
     *
     * @param definition 事务定义
     * @return 事务状态
     * @throws TransactionException 事务异常
     */
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;

    /**
     * 提交事务
     *
     * @param status 事务状态
     * @throws TransactionException 事务异常
     */
    void commit(TransactionStatus status) throws TransactionException;

    /**
     * 回滚事务
     *
     * @param status 事务状态
     * @throws TransactionException 事务异常
     */
    void rollback(TransactionStatus status) throws TransactionException;
}

3.9.2 事务管理器平台抽象类

AbstractPlatformTransactionManager.java

package com.lino.springframework.tx.transaction.support;

import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import com.lino.springframework.tx.transaction.TransactionDefinition;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.TransactionStatus;
import java.io.Serializable;

/**
 * @description: 抽象事务管理器平台
 */
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {

    @Override
    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();
        if (null == definition) {
            definition = new DefaultTransactionDefinition();
        }
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new TransactionException("Invalid transaction timeout " + definition.getTimeout());
        }
        // 暂定事务传播为默认的行为
        DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true);
        // 开始事务
        doBegin(transaction, definition);
        return status;
    }

    private DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, Object transaction, boolean newTransaction) {
        return new DefaultTransactionStatus(transaction, newTransaction);
    }

    @Override
    public void commit(TransactionStatus status) throws TransactionException {
        if (status.isCompleted()) {
            throw new IllegalArgumentException(
                    "Transaction is already completed - do not call or rollback more than once per transaction");
        }
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        processCommit(defStatus);
    }

    private void processCommit(DefaultTransactionStatus status) throws TransactionException {
        doCommit(status);
    }

    @Override
    public void rollback(TransactionStatus status) throws TransactionException {
        if (status.isCompleted()) {
            throw new IllegalArgumentException(
                    "Transaction is already completed - do not call commit or rollback more than once per transaction");
        }
        DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
        processRollback(defStatus, false);
    }

    private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
        doRollback(status);
    }

    /**
     * 获取事务
     *
     * @return 事务
     * @throws TransactionException 事务异常
     */
    protected abstract Object doGetTransaction() throws TransactionException;

    /**
     * 开始事务
     *
     * @param transaction 事务对象
     * @param definition  事务定义
     * @throws TransactionException 事务异常
     */
    protected abstract void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException;

    /**
     * 提交事务
     *
     * @param status 事务状态
     * @throws TransactionException 事务异常
     */
    protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;

    /**
     * 回滚事务
     *
     * @param status 事务状态
     * @throws TransactionException 事务异常
     */
    protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
}
  • 在事务的拦截处理中看到,我们使用 TransactionAspectSupport#invokeWithinTransaction 对事务进行了获取、提交和回滚操作。
  • 这些功能都是来自 AbstractPlatformTransactionManager 抽象事务管理器平台,在这个类中可以设置事务的传播行为、事务的操作。
    • 事务的获取、提交、回滚操作也来自数据库连接池提供的功能。

3.9.3 事务处理

TransactionAspectSupport.java

package com.lino.springframework.tx.transaction.interceptor;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.threadlocal.NamedThreadLocal;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.beans.factory.BeanFactoryAware;
import com.lino.springframework.beans.factory.InitializingBean;
import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import com.lino.springframework.tx.transaction.TransactionStatus;
import com.lino.springframework.util.ClassUtils;
import java.lang.reflect.Method;

/**
 * @description: 事务切面
 */
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {

    private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
            new NamedThreadLocal<>("Current aspect-driven transaction");

    private BeanFactory beanFactory;

    private TransactionAttributeSource transactionAttributeSource;

    private PlatformTransactionManager transactionManager;

    protected Object invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation) throws Throwable {
        TransactionAttributeSource tas = getTransactionAttributeSource();
        // 查找事务注解 Transactional
        TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);

        PlatformTransactionManager manager = determineTransactionManager();
        String joinPointIdentification = methodIdentification(method, targetClass);
        TransactionInfo txInfo = createTransactionIfNecessary(manager, txAttr, joinPointIdentification);

        Object retVal = null;
        try {
            retVal = invocation.proceedWithInvocation();
        } catch (Throwable e) {
            completeTransactionAfterThrowing(txInfo, e);
            throw e;
        } finally {
            cleanupTransactionInfo(txInfo);
        }
        commitTransactionAfterReturning(txInfo);

        return retVal;
    }

    public TransactionAttributeSource getTransactionAttributeSource() {
        return transactionAttributeSource;
    }

    public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
        this.transactionAttributeSource = transactionAttributeSource;
    }

    public PlatformTransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    /**
     * 当前使用DataSourceTransactionManager
     */
    protected PlatformTransactionManager determineTransactionManager() {
        return getTransactionManager();
    }

    /**
     * 获取目标方法的唯一标识
     */
    private String methodIdentification(Method method, Class<?> targetClass) {
        return ClassUtils.getQualifiedMethodName(method, targetClass);
    }

    protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinPointIdentification) {
        if (txAttr != null && txAttr.getName() == null) {
            txAttr = new DelegatingTransactionAttribute(txAttr) {
                @Override
                public String getName() {
                    return joinPointIdentification;
                }
            };
        }

        TransactionStatus status = null;
        if (txAttr != null) {
            if (tm != null) {
                status = tm.getTransaction(txAttr);
            }
        }
        return prepareTransactionInfo(tm, txAttr, joinPointIdentification, status);
    }

    protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinPointIdentification, TransactionStatus status) {
        TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinPointIdentification);
        if (txAttr != null) {
            txInfo.newTransactionStatus(status);
        }
        txInfo.bindToThread();
        return txInfo;
    }

    protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
        if (null != txInfo && null != txInfo.getTransactionStatus()) {
            if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            } else {
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
        }
    }

    protected void cleanupTransactionInfo(TransactionInfo txInfo) {
        if (null != txInfo) {
            txInfo.restoreThreadLocalStatus();
        }
    }

    protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
        if (null != txInfo && null != txInfo.getTransactionStatus()) {
            txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
        }
    }

    protected interface InvocationCallback {
        Object proceedWithInvocation() throws Throwable;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    protected final class TransactionInfo {

        private final PlatformTransactionManager transactionManager;
        private final TransactionAttribute transactionAttribute;
        private final String joinPointIdentification;
        private TransactionStatus transactionStatus;
        private TransactionInfo oldTransactionInfo;

        public TransactionInfo(PlatformTransactionManager transactionManager,
                               TransactionAttribute transactionAttribute, String joinpointIdentification) {
            this.transactionManager = transactionManager;
            this.transactionAttribute = transactionAttribute;
            this.joinPointIdentification = joinpointIdentification;
        }

        public PlatformTransactionManager getTransactionManager() {
            Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
            return transactionManager;
        }

        public String getJoinPointIdentification() {
            return joinPointIdentification;
        }

        public TransactionAttribute getTransactionAttribute() {
            return transactionAttribute;
        }

        public void newTransactionStatus(TransactionStatus status) {
            this.transactionStatus = status;
        }

        public TransactionStatus getTransactionStatus() {
            return transactionStatus;
        }

        public boolean hasTransaction() {
            return null != this.transactionStatus;
        }

        private void bindToThread() {
            this.oldTransactionInfo = transactionInfoHolder.get();
            transactionInfoHolder.set(this);
        }

        private void restoreThreadLocalStatus() {
            transactionInfoHolder.set(this.oldTransactionInfo);
        }

    }
}
  • 方法拦截器中的 invokeWithinTransaction 方法,就是 TransactionAspectSupport#invokeWithinTransaction 提供的具体拦截操作。
    • invokeWithinTransaction 方法中的操作包括:提取注解方法、开启事务的提交和回滚,以及把方法包装到 invocation.proceedWithInvocation 中并提交。
  • 到这里,已经完成了最基本的事务注解查找到提交的链路。

3.9.4 事务拦截器

TransactionInterceptor.java

package com.lino.springframework.tx.transaction.interceptor;

import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.io.Serializable;

/**
 * @description: 事务拦截器
 */
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {

    public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource transactionAttributeSource) {
        setTransactionManager(ptm);
        setTransactionAttributeSource(transactionAttributeSource);
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return invokeWithinTransaction(invocation.getMethod(), invocation.getThis().getClass(), invocation::proceed);
    }
}
  • 因为需要使用一个被 AOP 拦截操作处理事务的类,所以需要实现一个事务拦截功能。

3.9.5 事务同步管理器

TransactionSynchronizationManager.java

package com.lino.springframework.tx.transaction.support;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.threadlocal.NamedThreadLocal;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 事务同步管理器
 */
public abstract class TransactionSynchronizationManager {

    /**
     * 当前线程的连接存储
     */
    private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
    /**
     * 事务的名称
     */
    private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name");

    /**
     * 事务是否是只读
     */
    private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status");

    /**
     * 事务的隔离级别
     */
    private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level");

    /**
     * 事务是否开启
     */
    private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");

    public static Object getResource(Object key) {
        return doGetResource(key);
    }

    private static Object doGetResource(Object actualKey) {
        Map<Object, Object> map = resources.get();
        if (null == map) {
            return null;
        }
        return map.get(actualKey);
    }

    public static void bindResource(Object key, Object value) throws IllegalStateException {
        Assert.notNull(value, "Value must not be null");
        Map<Object, Object> map = resources.get();
        if (null == map) {
            map = new HashMap<>(16);
            resources.set(map);
        }
        map.put(key, value);
    }
}
  • TransactionSynchronizationManager 类主要使用 ThreadLocal 来记录本地线程的特性,保存一次线程下的 DB 链接,便于用户操作数据,以及保证 Spring 管理事务时获取的是同一个链接。否则在不同链接下,就不能再提交事务了。

3.10 获取数据库连接

3.10.1 数据库连接处理器接口

ConnectionHandle.java

package com.lino.springframework.jdbc.datasource;

import java.sql.Connection;

/**
 * @description: 数据库连接处理器接口
 */
public interface ConnectionHandle {

    /**
     * 获取连接
     *
     * @return 连接
     */
    Connection getConnection();

    /**
     * 释放连接
     *
     * @param con 连接
     */
    default void releaseConnection(Connection con) {}
}

3.10.2 简单数据库连接处理器

SimpleConnectionHandle.java

package com.lino.springframework.jdbc.datasource;

import cn.hutool.core.lang.Assert;
import java.sql.Connection;

/**
 * @description: 简单数据库连接处理器
 */
public class SimpleConnectionHandle implements ConnectionHandle {

    private final Connection connection;

    public SimpleConnectionHandle(Connection connection) {
        Assert.notNull(connection, "Connection must not be null");
        this.connection = connection;
    }

    @Override
    public Connection getConnection() {
        return this.connection;
    }
}

3.10.3 数据库连接处理器

ConnectionHolder.java

package com.lino.springframework.jdbc.datasource;

import cn.hutool.core.lang.Assert;
import java.sql.Connection;

/**
 * @description: 数据库连接处理器
 */
public class ConnectionHolder {

    private ConnectionHandle connectionHandle;

    private Connection currentConnection;

    public ConnectionHolder(ConnectionHandle connectionHandle) {
        this.connectionHandle = connectionHandle;
    }

    public ConnectionHolder(Connection connection) {
        this.connectionHandle = new SimpleConnectionHandle(connection);
    }

    public ConnectionHandle getConnectionHandle() {
        return connectionHandle;
    }

    protected boolean hasConnection() {
        return this.connectionHandle != null;
    }

    protected void setConnection(Connection connection) {
        if (null != this.currentConnection) {
            if (null != this.connectionHandle) {
                this.connectionHandle.releaseConnection(this.currentConnection);
            }
            this.currentConnection = null;
        }
        if (null != connection) {
            this.connectionHandle = new SimpleConnectionHandle(connection);
        } else {
            this.connectionHandle = null;
        }
    }

    protected Connection getConnection() {
        Assert.notNull(this.connectionHandle, "Active connection is required.");
        if (null == this.currentConnection) {
            this.currentConnection = this.connectionHandle.getConnection();
        }
        return this.currentConnection;
    }
}

3.10.4 JDBC事务支撑对象

JdbcTransactionObjectSupport.java

package com.lino.springframework.jdbc.datasource;

/**
 * @description: JDBC事务对象
 */
public abstract class JdbcTransactionObjectSupport {

    private ConnectionHolder connectionHolder;

    public void setConnectionHolder(ConnectionHolder connectionHolder) {
        this.connectionHolder = connectionHolder;
    }

    public ConnectionHolder getConnectionHolder() {
        return connectionHolder;
    }

    public boolean hasConnectionHolder() {
        return null != this.connectionHolder;
    }
}

3.10.5 数据源事务管理器

DataSourceTransactionManager.java

package com.lino.springframework.jdbc.datasource;

import cn.hutool.core.lang.Assert;
import com.lino.springframework.beans.factory.InitializingBean;
import com.lino.springframework.tx.transaction.CannotCreateTransactionException;
import com.lino.springframework.tx.transaction.TransactionDefinition;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.support.AbstractPlatformTransactionManager;
import com.lino.springframework.tx.transaction.support.DefaultTransactionStatus;
import com.lino.springframework.tx.transaction.support.TransactionSynchronizationManager;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * @description: 数据源事务管理器
 */
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements InitializingBean {

    private DataSource dataSource;

    public DataSourceTransactionManager() {
    }

    public DataSourceTransactionManager(DataSource dataSource) {
        setDataSource(dataSource);
        afterPropertiesSet();
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public DataSource getDataSource() {
        return dataSource;
    }

    protected DataSource obtainDataSource() {
        DataSource dataSource = getDataSource();
        Assert.notNull(dataSource, "No DataSource set");
        return dataSource;
    }

    @Override
    protected Object doGetTransaction() throws TransactionException {
        DataSourceTransactionObject txObject = new DataSourceTransactionObject();
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
        txObject.setConnectionHolder(conHolder, false);
        return txObject;
    }

    @Override
    protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        try {
            con.commit();
        } catch (SQLException e) {
            throw new TransactionException("Could not commit JDBC transaction", e);
        }
    }

    @Override
    protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
        Connection con = txObject.getConnectionHolder().getConnection();
        try {
            con.rollback();
        } catch (SQLException e) {
            throw new TransactionException("Could not roll back JDBC transaction", e);
        }
    }

    @Override
    protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
        DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
        Connection con = null;
        try {
            Connection newCon = obtainDataSource().getConnection();
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);

            con = txObject.getConnectionHolder().getConnection();
            if (con.getAutoCommit()) {
                con.setAutoCommit(false);
            }
            prepareTransactionalConnection(con, definition);

            TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());

        } catch (SQLException e) {
            try {
                assert con != null;
                con.close();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            txObject.setConnectionHolder(null, false);
            throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", e);
        }

    }

    @Override
    public void afterPropertiesSet() {
        if (null == getDataSource()) {
            throw new IllegalArgumentException("Property 'datasource' is required");
        }
    }

    protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition) throws SQLException {
        if (definition.isReadOnly()) {
            try (Statement stmt = con.createStatement()) {
                stmt.execute("set transaction read only");
            }
        }
    }

    public static class DataSourceTransactionObject extends JdbcTransactionObjectSupport {
        private boolean newConnectionHolder;
        private boolean mustRestoreAutoCommit;

        public void setConnectionHolder(ConnectionHolder connectionHolder, boolean newConnectionHolder) {
            super.setConnectionHolder(connectionHolder);
            this.newConnectionHolder = newConnectionHolder;
        }
    }
}

3.10.6 数据源操作抽象类

DataSourceUtils.java

package com.lino.springframework.jdbc.datasource;

import com.lino.springframework.jdbc.CannotGetJdbcConnectionException;
import com.lino.springframework.tx.transaction.support.TransactionSynchronizationManager;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * @description: 数据源操作抽象类
 */
public abstract class DataSourceUtils {

    /**
     * 获取数据库连接
     *
     * @param dataSource 数据库对象
     * @return 数据库连接
     */
    public static Connection getConnection(DataSource dataSource) {
        try {
            return doGetConnection(dataSource);
        } catch (SQLException e) {
            throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", e);
        }
    }

    /**
     * Actually obtain a JDBC Connection from the given DataSource.
     * Same as {@link #getConnection}, but throwing the original SQLException.
     * 

Is aware of a corresponding Connection bound to the current thread, for example * when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread * if transaction synchronization is active (e.g. if in a JTA transaction). * * @param dataSource the DataSource to obtain Connections from * @return a JDBC Connection from the given DataSource * @throws SQLException if thrown by JDBC methods * @see #doReleaseConnection */ public static Connection doGetConnection(DataSource dataSource) throws SQLException { ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource); if (null != conHolder && conHolder.hasConnection()) { return conHolder.getConnection(); } return fetchConnection(dataSource); } private static Connection fetchConnection(DataSource dataSource) throws SQLException { Connection conn = dataSource.getConnection(); if (null == conn) { throw new IllegalArgumentException("DataSource return null from getConnection():" + dataSource); } return conn; } public static void releaseConnection(Connection con, DataSource dataSource) { try { doReleaseConnection(con, dataSource); } catch (Exception ignore) { } } public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException { if (con == null) { return; } doCloseConnection(con, dataSource); } public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException { con.close(); } }

  • DataSourceUtils 数据源工具操作类中,最初只是用 dataSource.getConnection 方法获取数据源链接。
  • 为了扩展事务的功能,需要使用 TransactionSynchronizationManager.getResource(dataSource) 存储数据源链接,便于操作事务时获取同一个链接。

3.10.7 JDBC操作模板

JdbcTemplate.java

package com.lino.springframework.jdbc.core;

import cn.hutool.core.lang.Assert;
import com.lino.springframework.jdbc.UncategorizedSQLException;
import com.lino.springframework.jdbc.datasource.DataSourceUtils;
import com.lino.springframework.jdbc.support.JdbcAccessor;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

/**
 * @description: JDBC 操作模板
 */
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
    /**
     * 查询大小
     */
    private int fetchSize = -1;
    /**
     * 最大行数
     */
    private int maxRows = -1;
    /**
     * 查询时间
     */
    private int queryTimeout = -1;

    ...

    @Override
    public <T> T execute(StatementCallback<T> action) {
        Connection con = DataSourceUtils.getConnection(obtainDataSource());
        try {
            Statement stmt = con.createStatement();
            applyStatementSettings(stmt);
            return action.doInStatement(stmt);
        } catch (SQLException ex) {
            throw new UncategorizedSQLException("ConnectionCallback", getSql(action), ex);
        }
    }

    private static String getSql(Object sqlProvider) {
        if (sqlProvider instanceof SqlProvider) {
            return ((SqlProvider) sqlProvider).getSql();
        } else {
            return null;
        }
    }

    ...
}
  • 修改了 execute 方法中,添加了 getSql 方法。

四、测试:事务处理

4.1 添加测试配置

4.1.1 事务测试类

JdbcService.java

package com.lino.springframework.test.bean;

import com.lino.springframework.jdbc.core.JdbcTemplate;
import com.lino.springframework.tx.transaction.annotation.Transactional;

/**
 * @description: JDBC服务类
 */
public class JdbcService {

    @Transactional(rollbackFor = Exception.class)
    public void saveData(JdbcTemplate jdbcTemplate) {
        jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
        jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
    }

    public void saveDataNoTransaction(JdbcTemplate jdbcTemplate) {
        jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
        jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
    }
}
  • 模拟一个用户操作事务的 Bean 对象有两种方法,一种是使用 @Transactional 注解管理事务,另一种是不使用 @Transactional 注解管理事务。
  • 按照预期,使用事务注解提交两条一样的语句,数据库不会有任何记录,因为存在主键冲突。没有使用事务注解的语句会成功提交一条记录。

4.1.2 Spring属性配置文件

spring.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	         http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false&serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    bean>

    <bean id="jdbcTemplate" class="com.lino.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    bean>

    <bean id="jdbcService" class="com.lino.springframework.test.bean.JdbcService"/>

beans>
  • spring.xml 配置文件中,先配置数据库的链接信息及库表。
    • 再将 dataSource 注入 JdbcTemplate 中,由 JdbcTemplate 完成数据库的操作。
  • 配置 jdbcService Bean 对象是一个用于操作验证数据库事务的方法。

4.1.3 初始化测试

ApiTest.java

package com.lino.springframework.test;

import com.alibaba.druid.pool.DruidDataSource;
import com.lino.springframework.aop.AdvisedSupport;
import com.lino.springframework.aop.TargetSource;
import com.lino.springframework.aop.aspectj.AspectJExpressionPointcut;
import com.lino.springframework.aop.framework.Cglib2AopProxy;
import com.lino.springframework.context.support.ClassPathXmlApplicationContext;
import com.lino.springframework.jdbc.core.JdbcTemplate;
import com.lino.springframework.jdbc.datasource.DataSourceTransactionManager;
import com.lino.springframework.test.bean.JdbcService;
import com.lino.springframework.tx.transaction.annotation.AnnotationTransactionAttributeSource;
import com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor;
import org.junit.Before;
import org.junit.Test;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;

/**
 * @description: 测试类
 */
public class ApiTest {

    private JdbcTemplate jdbcTemplate;
    private JdbcService jdbcService;
    private DataSource dataSource;

    @Before
    public void init() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
        dataSource = applicationContext.getBean(DruidDataSource.class);
        jdbcService = applicationContext.getBean(JdbcService.class);
    }
}
  • init 方法是一个初始化操作,用于获取需要的 Bean 对象。

4.2 单元测试

4.2.1 有事务测试

ApiTest.java

@Test
public void test_Transaction() throws SQLException {
    AnnotationTransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
    transactionAttributeSource.findTransactionAttribute(jdbcService.getClass());

    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
    TransactionInterceptor interceptor = new TransactionInterceptor(transactionManager, transactionAttributeSource);

    // 组装代理对象
    AdvisedSupport advisedSupport = new AdvisedSupport();
    advisedSupport.setTargetSource(new TargetSource(jdbcService));
    advisedSupport.setMethodInterceptor(interceptor);
    advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* com.lino.springframework.test.bean.JdbcService.*(..))"));

    // 代理对象(Cglib2AopProxy)
    JdbcService proxy_cglib = (JdbcService) new Cglib2AopProxy(advisedSupport).getProxy();

    // 测试调用,有事务【不能同时提交2条有主键冲突的数据】
    proxy_cglib.saveData(jdbcTemplate);
}

测试结果

信息: {dataSource-1} inited

com.lino.springframework.jdbc.UncategorizedSQLException: insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())

	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:75)
	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:102)
	at com.lino.springframework.test.bean.JdbcService.saveData(JdbcService.java:16)
	at com.lino.springframework.test.bean.JdbcService$$FastClassByCGLIB$$d07274b8.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at com.lino.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.proceed(Cglib2AopProxy.java:61)
	at com.lino.springframework.tx.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:42)
	at com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:23)
	at com.lino.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:44)
	at com.lino.springframework.test.bean.JdbcService$$EnhancerByCGLIB$$aac0f4c.saveData(<generated>)
	at com.lino.springframework.test.ApiTest.test_Transaction(ApiTest.java:59)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
	at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
	at com.alibaba.druid.pool.DruidPooledStatement.execute(DruidPooledStatement.java:644)
	at com.lino.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:98)
	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:73)
	... 35 more

在这里插入图片描述

  • 使用事务注解的测试结果为报错主键冲突,数据库不会有任何记录。

4.2.2 无事务测试

ApiTest.java

@Test
public void test_Transaction() throws SQLException {
    AnnotationTransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
    transactionAttributeSource.findTransactionAttribute(jdbcService.getClass());

    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
    TransactionInterceptor interceptor = new TransactionInterceptor(transactionManager, transactionAttributeSource);

    // 组装代理对象
    AdvisedSupport advisedSupport = new AdvisedSupport();
    advisedSupport.setTargetSource(new TargetSource(jdbcService));
    advisedSupport.setMethodInterceptor(interceptor);
    advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* com.lino.springframework.test.bean.JdbcService.*(..))"));

    // 代理对象(Cglib2AopProxy)
    JdbcService proxy_cglib = (JdbcService) new Cglib2AopProxy(advisedSupport).getProxy();

    // 测试调用,无事务【提交2条有主键冲突的数据,成功一条】
    proxy_cglib.saveDataNoTransaction(jdbcTemplate);

}

测试结果

信息: {dataSource-1} inited

com.lino.springframework.jdbc.UncategorizedSQLException: insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())

	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:75)
	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:102)
	at com.lino.springframework.test.bean.JdbcService.saveDataNoTransaction(JdbcService.java:21)
	at com.lino.springframework.test.bean.JdbcService$$FastClassByCGLIB$$d07274b8.invoke(<generated>)
	at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
	at com.lino.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.proceed(Cglib2AopProxy.java:61)
	at com.lino.springframework.tx.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:42)
	at com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:23)
	at com.lino.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:44)
	at com.lino.springframework.test.bean.JdbcService$$EnhancerByCGLIB$$aac0f4c.saveDataNoTransaction(<generated>)
	at com.lino.springframework.test.ApiTest.test_Transaction(ApiTest.java:62)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
	at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
	at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
	at com.alibaba.druid.pool.DruidPooledStatement.execute(DruidPooledStatement.java:644)
	at com.lino.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:98)
	at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:73)
	... 35 more

在这里插入图片描述

  • 没有使用事务注解同样会出现主键冲突的错误,但是数据库中会添加一条新插入的记录。

五、总结:事务处理

  • 先了解事务的特性及其最基本的使用流程,再思考如何把事务拆解到 AOP 中进行包装。这个过程也是对一个复杂问题的细化分析和处理。
  • 关于事务的操作,定义了事务注解,并在 invokeWithinTransaction 方法中使用和提取。
    • 并用此方法控制事务,包括获取事务、提交事务、回滚事务等。
  • ThreadLocal 是一个非常重要的知识点。如果没有使用统一的数据库链接,则无法做到用户的使用链接和切面链接保持为同一个,也就不能提交事务。

你可能感兴趣的:(手写spring,spring,java)