ShardingJDBC源码阅读(十一)事务与SpringAOP

2,021 阅读12分钟

前言

本章学习ShardingJDBC事务原理。

  • 事务分类
  • 本地事务
  • 全局事务:事务引擎、事务管理器、声明式事务
  • ShardingJDBC如何结合SpringAOP实现注解式事务

一、支持事务类型

public enum TransactionType {
    LOCAL, XA, BASE
}

目前Sharding-JDBC分为三种事务:

  • LOCAL:本地,不支持分布式事务。
  • XA:XA事务,两阶段提交,强一致。默认使用atomikos框架实现。
  • BASE:BASE柔性事务,最终一致,使用Seata实现。

二、本地事务

本地事务,在用户执行ShardingConnection.setAutoCommit(false)时会记录下来。见ShardingConnection父类AbstractConnectionAdapter#setAutoCommit的实现。

// ShardingConnection持有的缓存连接
private final Multimap<String, Connection> cachedConnections = LinkedHashMultimap.create();
@Override
public void setAutoCommit(final boolean autoCommit) throws SQLException {
    this.autoCommit = autoCommit;
    setAutoCommitForLocalTransaction(autoCommit);
}

private void setAutoCommitForLocalTransaction(final boolean autoCommit) throws SQLException {
    // 记录本次方法调用
    recordMethodInvocation(Connection.class, "setAutoCommit", new Class[]{boolean.class}, new Object[]{autoCommit});
    // 如果有缓存的连接,直接执行
    forceExecuteTemplate.execute(cachedConnections.values(), connection -> connection.setAutoCommit(autoCommit));
}

recordMethodInvocation是由抽象父类WrapperAdapter实现的方法,记录一次方法调用,这个在ShardingJDBC源码阅读(二)创建ShardingDataSource提到过。

在用户执行ShardingPrepareStatement.execute(或executeUpdate)时,会创建真实的数据库连接,此时会重放recordMethodInvocation记录的所有反射调用。见AbstractConnectionAdapter#createConnections。

private List<Connection> createConnections(final String dataSourceName, final ConnectionMode connectionMode, final DataSource dataSource, final int connectionSize) throws SQLException {
    if (1 == connectionSize) {
        Connection connection = createConnection(dataSourceName, dataSource);
        // 重放connection的方法调用
        replayMethodsInvocation(connection);
        return Collections.singletonList(connection);
    }
    // 连接限制
    if (ConnectionMode.CONNECTION_STRICTLY == connectionMode) {
        return createConnections(dataSourceName, dataSource, connectionSize);
    }
    // 内存限制
    synchronized (dataSource) {
        return createConnections(dataSourceName, dataSource, connectionSize);
    }
}
    
private List<Connection> createConnections(final String dataSourceName, final DataSource dataSource, final int connectionSize) throws SQLException {
    List<Connection> result = new ArrayList<>(connectionSize);
    for (int i = 0; i < connectionSize; i++) {
            Connection connection = createConnection(dataSourceName, dataSource);
            // 重放connection的方法调用
            replayMethodsInvocation(connection);
            result.add(connection);
    }
    return result;
}

replayMethodsInvocation是由抽象父类WrapperAdapter实现的方法,通过反射重放recordMethodInvocation记录的方法调用。见ShardingJDBC源码阅读(二)创建ShardingDataSource

最终在用户调用ShardingConnection的commit方法时,循环所有实际Connection执行commit方法。见ShardingConnection父类AbstractConnectionAdapter的commit实现。

@Override
public void commit() throws SQLException {
    forceExecuteTemplate.execute(cachedConnections.values(), Connection::commit);
}

三、事务管理引擎

ShardingTransactionManagerEngine事务管理引擎,负责创建事务管理器和事务管理器的初始化,并对外提供获取事务管理器的方法

加载事务管理引擎的时机

创建ShardingDataSource时,需要创建ShardingRuntimeContext上下文。而ShardingRuntimeContext创建时即创建了ShardingTransactionManagerEngine,并执行引擎的初始化方法。

public final class ShardingRuntimeContext extends MultipleDataSourcesRuntimeContext<ShardingRule> {
    
    private final CachedDatabaseMetaData cachedDatabaseMetaData;
    
    private final ShardingTransactionManagerEngine shardingTransactionManagerEngine;
    
    public ShardingRuntimeContext(final Map<String, DataSource> dataSourceMap, final ShardingRule shardingRule, final Properties props, final DatabaseType databaseType) throws SQLException {
        super(dataSourceMap, shardingRule, props, databaseType);
        cachedDatabaseMetaData = createCachedDatabaseMetaData(dataSourceMap);
        // 实例化ShardingTransactionManagerEngine
        shardingTransactionManagerEngine = new ShardingTransactionManagerEngine();
        // 初始化ShardingTransactionManagerEngine
        shardingTransactionManagerEngine.init(databaseType, dataSourceMap);
    }
}

加载并初始化事务管理引擎

ShardingTransactionManagerEngine构造时通过SPI机制加载ShardingTransactionManager实例,放入transactionManagerMap。在同一个ShardingDataSource中,同一个TransactionType,只会有一个ShardingTransactionManager实例,一般可以认为ShardingTransactionManager实例以单例存在。

public final class ShardingTransactionManagerEngine {
    // TransactionType - ShardingTransactionManager
    private final Map<TransactionType, ShardingTransactionManager> transactionManagerMap = new EnumMap<>(TransactionType.class);
    
    public ShardingTransactionManagerEngine() {
        loadShardingTransactionManager();
    }

    // SPI加载ShardingTransactionManager
    private void loadShardingTransactionManager() {
        for (ShardingTransactionManager each : ServiceLoader.load(ShardingTransactionManager.class)) {
            // 同样的TransactionType取先加载到的ShardingTransactionManager实现
            if (transactionManagerMap.containsKey(each.getTransactionType())) {
                continue;
            }
            transactionManagerMap.put(each.getTransactionType(), each);
        }
    }
}

ShardingTransactionManagerEngine初始化,循环执行所有ShardingTransactionManager的init方法,将原始的DataSource封装为ResourceDataSource传入。

public void init(final DatabaseType databaseType, final Map<String, DataSource> dataSourceMap) {
    for (Entry<TransactionType, ShardingTransactionManager> entry : transactionManagerMap.entrySet()) {
        Collection<ResourceDataSource> resourceDataSources = getResourceDataSources(dataSourceMap);
        // 执行ShardingTransactionManager的init方法
        entry.getValue().init(databaseType, resourceDataSources);
    }
}

ResourceDataSource封装真实数据源和逻辑数据源名称,并额外提供了DataSource的唯一标识。

@Getter
public final class ResourceDataSource {
    // 逻辑数据源名称
    private final String originalName;
    // 唯一标识
    private String uniqueResourceName;
    // 真实数据源
    private final DataSource dataSource;
    
    public ResourceDataSource(final String originalName, final DataSource dataSource) {
        this.originalName = originalName;
        this.dataSource = dataSource;
        this.uniqueResourceName = ResourceIDGenerator.getInstance().nextId() + originalName;
    }
}

事务管理引擎做了什么

事务管理引擎主要的作用就是根据外部提供的事务类型返回对应的事务管理器(策略)。如果是本地事务,返回为空。

public ShardingTransactionManager getTransactionManager(final TransactionType transactionType) {
    ShardingTransactionManager result = transactionManagerMap.get(transactionType);
    if (TransactionType.LOCAL != transactionType) {
        Preconditions.checkNotNull(result, "Cannot find transaction manager of [%s]", transactionType);
    }
    return result;
}

三、事务管理器

ShardingTransactionManager事务管理器,对于非本地事务,无论是XA还是BASE,都需要实现这个接口提供分布式事务管理。

public interface ShardingTransactionManager extends AutoCloseable {
    
    void init(DatabaseType databaseType, Collection<ResourceDataSource> resourceDataSources);
    
    TransactionType getTransactionType();
    
    boolean isInTransaction();
    
    Connection getConnection(String dataSourceName) throws SQLException;
    
    void begin();
    
    void commit();
    
    void rollback();
}

1、方法

  • init:事务管理器初始化,在创建ShardingDataSource时即会调用。
  • getTransactionType:返回支持的事务类型,提供给事务管理引擎,做策略。
  • isInTransaction:判断是否处于事务中。
  • getConnection:获取Connection。
  • begin、commit、rollback:开启、提交、回滚事务。

重点看几个方法的调用时机。

isInTransaction

isInTransaction在ShardingConnection设置autocommit时被调用,主要用于判断是否需要执行事务管理器的commit和begin方法。

public final class ShardingConnection extends AbstractConnectionAdapter {
    
    private final TransactionType transactionType;
    
    private final ShardingTransactionManager shardingTransactionManager;
    
    @Override
    public void setAutoCommit(final boolean autoCommit) throws SQLException {
        // 本地事务
        if (TransactionType.LOCAL == transactionType) {
            super.setAutoCommit(autoCommit);
            return;
        }
        // 重复的开启或提交事务,不需要执行任何操作
        if (autoCommit && !shardingTransactionManager.isInTransaction() || !autoCommit && shardingTransactionManager.isInTransaction()) {
            return;
        }
        // 提交事务
        if (autoCommit && shardingTransactionManager.isInTransaction()) {
            shardingTransactionManager.commit();
            return;
        }
        // 开启事务
        if (!autoCommit && !shardingTransactionManager.isInTransaction()) {
            closeCachedConnections();
            shardingTransactionManager.begin();
        }
    }
}

getConnection

getConnection方法一定是在解析、路由、重写三个步骤结束之后执行,在执行阶段需要获取Connection。ShardingPreparedStatement#execute方法的initPreparedStatementExecutor步骤会获取连接。

public boolean execute() throws SQLException {
  try {
      // 资源清理
      clearPrevious();
      // 解析 路由 重写
      prepare();
      // 初始化PreparedStatementExecutor 这一步会获取连接
      initPreparedStatementExecutor();
      // 执行SQL
      return preparedStatementExecutor.execute();
  } finally {
      // 资源清理
      clearBatch();
  }
}

如果ShardingConnection中缓存的AbstractConnectionAdapter#cachedConnections不足以执行SQL,最终会调用ShardingConnection#createConnection。

protected Connection createConnection(final String dataSourceName, final DataSource dataSource) throws SQLException {
    return isInShardingTransaction() ? shardingTransactionManager.getConnection(dataSourceName) : dataSource.getConnection();
}

begin、commit、rollback

begin:用户调用ShardingConnection#setAutoCommit时触发。

commit:用户调用ShardingConnection#commit时触发。

public void commit() throws SQLException {
    if (TransactionType.LOCAL == transactionType) {
        super.commit();
    } else {
        shardingTransactionManager.commit();
    }
}

rollback:用户调用ShardingConnection#rollback时触发。

public void rollback() throws SQLException {
  if (TransactionType.LOCAL == transactionType) {
      super.rollback();
  } else {
      shardingTransactionManager.rollback();
  }
}

2、实现

XAShardingTransactionManager

XAShardingTransactionManager负责XA事务管理实现。

public final class XAShardingTransactionManager implements ShardingTransactionManager {
    // 逻辑数据源名 - XATransactionDataSource
    private final Map<String, XATransactionDataSource> cachedDataSources = new HashMap<>();
    // SPI机制加载XATransactionManager的实现
    private final XATransactionManager xaTransactionManager = XATransactionManagerLoader.getInstance().getTransactionManager();
    
    @Override
    public void init(final DatabaseType databaseType, final Collection<ResourceDataSource> resourceDataSources) {
        for (ResourceDataSource each : resourceDataSources) {
            XATransactionDataSource dataSource = new XATransactionDataSource(databaseType, each.getUniqueResourceName(), each.getDataSource(), xaTransactionManager);
            cachedDataSources.put(each.getOriginalName(), dataSource);
        }
        xaTransactionManager.init();
    }
    
    @Override
    public TransactionType getTransactionType() {
        return TransactionType.XA;
    }
    
    @Override
    public boolean isInTransaction() {
        return Status.STATUS_NO_TRANSACTION != xaTransactionManager.getTransactionManager().getStatus();
    }
    
    @Override
    public Connection getConnection(final String dataSourceName) throws SQLException {
        return cachedDataSources.get(dataSourceName).getConnection();
    }
    
    @Override
    public void begin() {
        xaTransactionManager.getTransactionManager().begin();
    }
    
    @Override
    public void commit() {
        xaTransactionManager.getTransactionManager().commit();
    }
    
    @Override
    public void rollback() {
        xaTransactionManager.getTransactionManager().rollback();
    }
    
    @Override
    public void close() throws Exception {
        for (XATransactionDataSource each : cachedDataSources.values()) {
            each.close();
        }
        cachedDataSources.clear();
        xaTransactionManager.close();
    }
}

XAShardingTransactionManager的事务控制(begin、commit、rollback)其实是委托org.apache.shardingsphere.transaction.xa.spi.XATransactionManager持有的javax.transaction.TransactionManager处理。XATransactionManager其实就是sharding-jdbc通往jta事务处理的适配层,由SPI加载XATransactionManager的实现,默认是AtomikosTransactionManager(忽略代码)。

XAShardingTransactionManager获取连接,是委托org.apache.shardingsphere.transaction.xa.jta.datasource.XATransactionDataSource实现。

public final class XATransactionDataSource implements AutoCloseable {
  // 数据库类型
  private final DatabaseType databaseType;
  // 数据源唯一标识
  private final String resourceName;
  // 实际数据源
  private final DataSource dataSource;
  // javax.sql.XADataSource
  private XADataSource xaDataSource;
  // org.apache.shardingsphere.transaction.xa.spi.XATransactionManager
  private XATransactionManager xaTransactionManager;

  public XATransactionDataSource(final DatabaseType databaseType, final String resourceName, final DataSource dataSource, final XATransactionManager xaTransactionManager) {
      this.databaseType = databaseType;
      this.resourceName = resourceName;
      this.dataSource = dataSource;
      this.xaDataSource = XADataSourceFactory.build(databaseType, dataSource);
      this.xaTransactionManager = xaTransactionManager;
      xaTransactionManager.registerRecoveryResource(resourceName, xaDataSource);
  }

  public Connection getConnection() throws SQLException, SystemException, RollbackException {
      // ...
      // 实际数据源(HikariDataSource)获取实际连接(HikariProxyConnection)
      Connection result = dataSource.getConnection();
      // javax.sql.XADataSource + java.sql.Connection + org.apache.shardingsphere.spi.database.type.DatabaseType - > javax.sql.XAConnection
      XAConnection xaConnection = XAConnectionFactory.createXAConnection(databaseType, xaDataSource, result);
      // org.apache.shardingsphere.transaction.xa.spi.XATransactionManager -> javax.transaction.TransactionManager -> javax.transaction.Transaction
      final Transaction transaction = xaTransactionManager.getTransactionManager().getTransaction();
      // ...
      // 实际连接
      return result;
  }
} 

可以看到XATransactionDataSource的getConnection方法将java.sql+shardingsphere结合转换到jta相关实现,返回的仍然是实际连接。(虽然连接池返回的连接也是代理对象,但是这里对于sharding-jdbc来说就是目标连接)

SeataATShardingTransactionManager

SeataATShardingTransactionManager负责BASE事务管理实现。

public final class SeataATShardingTransactionManager implements ShardingTransactionManager {
    
    private final Map<String, DataSource> dataSourceMap = new HashMap<>();
    
    private final String applicationId;
    
    private final String transactionServiceGroup;
    
    private final boolean enableSeataAT;
    
    public SeataATShardingTransactionManager() {
        FileConfiguration configuration = new FileConfiguration("seata.conf");
        enableSeataAT = configuration.getBoolean("sharding.transaction.seata.at.enable", true);
        applicationId = configuration.getConfig("client.application.id");
        transactionServiceGroup = configuration.getConfig("client.transaction.service.group", "default");
    }
    
    @Override
    public void init(final DatabaseType databaseType, final Collection<ResourceDataSource> resourceDataSources) {
        if (enableSeataAT) {
            // 初始化客户端
            initSeataRPCClient();
            for (ResourceDataSource each : resourceDataSources) {
                // 将原始dataSource转换为seata自己的DataSource代理实例
                dataSourceMap.put(each.getOriginalName(), new DataSourceProxy(each.getDataSource()));
            }
        }
    }
    
    private void initSeataRPCClient() {
        TMClient.init(applicationId, transactionServiceGroup);
        RMClient.init(applicationId, transactionServiceGroup);
    }
    
    @Override
    public TransactionType getTransactionType() {
        return TransactionType.BASE;
    }
    
    @Override
    public boolean isInTransaction() {
        return null != RootContext.getXID();
    }
    
    @Override
    public Connection getConnection(final String dataSourceName) throws SQLException {
        return dataSourceMap.get(dataSourceName).getConnection();
    }
    
    @Override
    public void begin() {
        GlobalTransaction globalTransaction = GlobalTransactionContext.getCurrentOrCreate();
        globalTransaction.begin();
        SeataTransactionHolder.set(globalTransaction);
    }
    
    @Override
    public void commit() {
        try {
            SeataTransactionHolder.get().commit();
        } finally {
            SeataTransactionHolder.clear();
            RootContext.unbind();
        }
    }
    
    @Override
    public void rollback() {
        try {
            SeataTransactionHolder.get().rollback();
        } finally {
            SeataTransactionHolder.clear();
            RootContext.unbind();
        }
    }
    
    @Override
    public void close() {
        dataSourceMap.clear();
        SeataTransactionHolder.clear();
        TmRpcClient.getInstance().destroy();
        RmRpcClient.getInstance().destroy();
    }
}
  • init:构建了RPCClient,将原始的DataSource转换为Seata自己的DataSource代理。
  • getConnection:返回Seata自己的DataSource代理DataSourceProxy创建的Connection(ConnectionProxy-seata自己的连接代理对象)。
  • isInTransaction:通过Seata的XID是否存在判断是否在全局事务中。
  • begin、commit、rollback:通过ThreadLocal获取当前全局事务,做相应的操作。

四、TransactionTypeHolder

TransactionTypeHolder通过ThreadLocal持有当前线程的全局事务类型。

public final class TransactionTypeHolder {
    
    private static final ThreadLocal<TransactionType> CONTEXT = ThreadLocal.withInitial(() -> TransactionType.LOCAL);
    
    public static TransactionType get() {
        return CONTEXT.get();
    }
    
    public static void set(final TransactionType transactionType) {
        CONTEXT.set(transactionType);
    }
    
    public static void clear() {
        CONTEXT.remove();
    }
}

如果要开启全局事务,需要在代码中通过TransactionTypeHolder.set方法设置,如下官方案例。

void insert() throws SQLException {
    // 设置全局事务类型为XA
    TransactionTypeHolder.set(TransactionType.XA);
    try (Connection connection = dataSource.getConnection()) {
        connection.setAutoCommit(false);
        PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO t_order (user_id, status) VALUES (?, ?)");
        doInsert(preparedStatement);
        connection.commit();
    }
}

首先通过TransactionTypeHolder.set(TransactionType.XA)设置当前线程的全局事务类型。然后ShardingDataSource获取ShardingConnection时,会根据当前事务类型,从事务引擎中获取对应的事务管理器。

public final class ShardingConnection extends AbstractConnectionAdapter {
    private final ShardingTransactionManager shardingTransactionManager;
    public ShardingConnection(final Map<String, DataSource> dataSourceMap, final ShardingRuntimeContext runtimeContext, final TransactionType transactionType) {
        // ...
        // 根据事务类型获取事务管理器
        shardingTransactionManager = runtimeContext.getShardingTransactionManagerEngine().getTransactionManager(transactionType);
    }
}

接下来无论是commit还是rollback都会根据当前线程的全局事务类型,走事务管理器ShardingTransactionManager做处理。

public final class ShardingConnection extends AbstractConnectionAdapter {
    
    private final TransactionType transactionType;
    
    private final ShardingTransactionManager shardingTransactionManager;
    @Override
    public void commit() throws SQLException {
        if (TransactionType.LOCAL == transactionType) {
            super.commit();
        } else {
            shardingTransactionManager.commit();
        }
    }

    @Override
    public void rollback() throws SQLException {
        if (TransactionType.LOCAL == transactionType) {
            super.rollback();
        } else {
            shardingTransactionManager.rollback();
        }
    }
}

五、ShardingTransactionType注解与SpringAOP

设置全局事务类型可以通过编码的方式,同样也可以使用注解+AOP的形式。

// 走org.springframework.transaction.interceptor.TransactionInterceptor
@Transactional
// 走org.apache.shardingsphere.transaction.spring.ShardingTransactionTypeInterceptor
@ShardingTransactionType(TransactionType.XA)
public TransactionType insert(final int count) {
    return jdbcTemplate.execute("INSERT INTO t_order_xa (user_id, status) VALUES (?, ?)", (PreparedStatementCallback<TransactionType>) preparedStatement -> {
        doInsert(count, preparedStatement);
        return TransactionTypeHolder.get();
    });
}

ThreadLocal+注解+AOP是比较常见的需求,ShardingJDBC利用ShardingTransactionType注解与SpringAOP结合作为starter引入,是个很好的范例。

ShardingJDBC仅仅注入了ShardingTransactionTypeScanner,就实现了这个需求。接下来看看它是怎么做到的。

@Bean
public ShardingTransactionTypeScanner shardingTransactionTypeScanner() {
    return new ShardingTransactionTypeScanner();
}

1、Advice

首先看一下ShardingJDBC对于Advice通知的实现,这里实现的是MethodInterceptor接口。

public final class ShardingTransactionTypeInterceptor implements MethodInterceptor {
    
    @Override
    public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
        ShardingTransactionType shardingTransactionType = getAnnotation(methodInvocation);
        // 设置ThreadLocal
        TransactionTypeHolder.set(shardingTransactionType.value());
        return methodInvocation.proceed();
    }
    
    private ShardingTransactionType getAnnotation(final MethodInvocation invocation) {
        // 获取目标对象的Class
        Class<?> targetClass = AopUtils.getTargetClass(invocation.getThis());
        // 获取方法上的注解
        ShardingTransactionType result = getMethodAnnotation(invocation, targetClass);
        // 优先使用方法上的注解value 其次选择类上的注解value
        return null != result ? result : targetClass.getAnnotation(ShardingTransactionType.class);
    }
    
    private ShardingTransactionType getMethodAnnotation(final MethodInvocation invocation, final Class<?> targetClass) {
        // 可能是个桥接方法
        Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
        // 转换为原始方法
        final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
        return userDeclaredMethod.getAnnotation(ShardingTransactionType.class);
    }
}

这里可以学几个Spring的工具类方法。举个例子,现在有一个SuperClass接口,和一个SubClass实现类。

public interface SuperClass<T> {
    T method(T t);
}

public class SubClass implements SuperClass<String> {

    @ShardingTransactionType
    @Override
    public String method(String s) {
        return s + "xxx";
    }
}

1、AopUtils#getTargetClass

根据代理对象获取到目标对象的Class。

SuperClass superClass = new SubClass();
//class org.apache.shardingsphere.example.transaction.xa.spring.boot.XAOrderService$SubClass
System.out.println(AopUtils.getTargetClass(superClass));

2、ClassUtils#getMostSpecificMethod

根据当前方法和目标Class,找到目标Class的这个方法。

SuperClass superClass = new SubClass();
//class org.apache.shardingsphere.example.transaction.xa.spring.boot.XAOrderService$SubClass
System.out.println(AopUtils.getTargetClass(superClass));

Method invocationMethod = superClass.getClass().getDeclaredMethod("method", Object.class);

Method method01 = ClassUtils.getMostSpecificMethod(invocationMethod, AopUtils.getTargetClass(superClass));
// public java.lang.Object org.apache.shardingsphere.example.transaction.xa.spring.boot.XAOrderService$SubClass.method(java.lang.Object)
System.out.println(method01);
Method method02 = ClassUtils.getMostSpecificMethod(invocationMethod, SuperClass.class);
// public abstract java.lang.Object org.apache.shardingsphere.example.transaction.xa.spring.boot.XAOrderService$SuperClass.method(java.lang.Object)
System.out.println(method02);

对于SubClass.class.getDeclaredMethod("method", Object.class)这个Method实例,当getMostSpecificMethod的第二个参数传SubClass.class时,返回的是SubClass对应的Method,当第二个参数传SuperClass.class时,返回的是SuperClass对应的Method。这个方法的含义就是,转换入参Method到目标Class对应的Method。

3、BridgeMethodResolver#findBridgedMethod

判断入参Method是否是桥接方法,如果是则返回实际方法;如果否则将入参Method原样返回。

什么是桥接方法?

编译器引入桥接方法与泛型的类型擦除有关。实际上SuperClass编译过后方法入参和出参都是Object,子类也需要实现这样的一个方法,这个实现方法由编译器生成。执行javap -c -p -v SubClass > SubClass.txt看一下字节码。

public java.lang.String method(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: new           #2                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
         7: aload_1
         8: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        11: ldc           #5                  // String xxx
        13: invokevirtual #4                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        16: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        19: areturn
      LineNumberTable:
        line 13: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      20     0  this   Lorg/apache/shardingsphere/example/transaction/xa/spring/boot/SubClass;
            0      20     1     s   Ljava/lang/String;
    RuntimeVisibleAnnotations:
      0: #24()

  public java.lang.Object method(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #7                  // class java/lang/String
         5: invokevirtual #8                  // Method method:(Ljava/lang/String;)Ljava/lang/String;
         8: areturn
      LineNumberTable:
        line 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lorg/apache/shardingsphere/example/transaction/xa/spring/boot/SubClass;
    RuntimeVisibleAnnotations:
      0: #24()

可以看到Object对应的桥接方法实际上是强转入参为String类型,然后调用了String入参的实际方法,得到结果后返回。大概的意思像下面这样。

public class SubClass implements SuperClass {

    @ShardingTransactionType
    public String method(String s) {
        return s + "xxx";
    }
    @Override
    public Object method(Object t) {
        return method((String)t);
    }
}

回到BridgeMethodResolver.findBridgedMethod这个方法,就很好理解了,如果入参Method是泛型擦除后的方法(桥接方法)就做转换,转换为泛型擦除前的方法(实际方法)。比如下面的案例。

// method01是个桥接方法
Method method01 = SubClass.class.getMethod("method", Object.class);
// true
System.out.println(method01.isBridge());
// 找到method01对应SubClass的Method
Method specificMethod = ClassUtils.getMostSpecificMethod(method01, SubClass.class);
// public java.lang.Object org.apache.shardingsphere.example.transaction.xa.spring.boot.XAOrderService$SubClass.method(java.lang.Object)
// 桥接方法
System.out.println(specificMethod);
// 将桥接方法转换为实际方法
Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// public java.lang.String org.apache.shardingsphere.example.transaction.xa.spring.boot.XAOrderService$SubClass.method(java.lang.String)
// 实际方法
System.out.println(userDeclaredMethod);

4、AopUtils#getMostSpecificMethod

ClassUtils.getMostSpecificMethod方法的文档注释如下。

Given a method, which may come from an interface, and a target class used in the current reflective invocation, find the corresponding target method if there is one. E.g. the method may be {@code IFoo.bar()} and the target class may be {@code DefaultFoo}. In this case, the method may be {@code DefaultFoo.bar()}. This enables attributes on that method to be found. NOTE: In contrast to {@link org.springframework.aop.support.AopUtils#getMostSpecificMethod}, this method does not resolve Java 5 bridge methods automatically. Call {@link org.springframework.core.BridgeMethodResolver#findBridgedMethod} if bridge method resolution is desirable (e.g. for obtaining metadata from the original method definition). NOTE: Since Spring 3.1.1, if Java security settings disallow reflective access (e.g. calls to {@code Class#getDeclaredMethods} etc, this implementation will fall back to returning the originally provided method.

大概意思就是描述了这个方法的功能,然后重点是,这个方法不支持对桥接方法的转换,如果要获取桥接方法对应的实际方法,有两个方案。一种是调用ClassUtils.getMostSpecificMethod之后通过BridgeMethodResolver#findBridgedMethod方法转换,另一种就是直接调用AopUtils#getMostSpecificMethod方法。

Method method01 = SubClass.class.getMethod("method", Object.class);
// public java.lang.String org.apache.shardingsphere.example.transaction.xa.spring.boot.XAOrderService$SubClass.method(java.lang.String)
System.out.println(AopUtils.getMostSpecificMethod(method01, SubClass.class));

2、Advisor

切点PointCut + 通知Advice = Advisor。ShardingTransactionTypeAdvisor继承AbstractPointcutAdvisor,实现切点和通知的get方法。

public final class ShardingTransactionTypeAdvisor extends AbstractPointcutAdvisor {
    
    private final Pointcut transactionTypePointcut;
    
    private final Advice transactionTypeInterceptor;
    
    ShardingTransactionTypeAdvisor() {
        Pointcut classPointcut = new ComposablePointcut(AnnotationMatchingPointcut.forClassAnnotation(ShardingTransactionType.class));
        Pointcut methodPointcut = new ComposablePointcut(AnnotationMatchingPointcut.forMethodAnnotation(ShardingTransactionType.class));
        transactionTypePointcut = new ComposablePointcut(classPointcut).union(methodPointcut);
        transactionTypeInterceptor = new ShardingTransactionTypeInterceptor();
        setOrder(Ordered.LOWEST_PRECEDENCE - 1);
    }
    
    @Override
    public Pointcut getPointcut() {
        return transactionTypePointcut;
    }
    
    @Override
    public Advice getAdvice() {
        return transactionTypeInterceptor;
    }
}

AnnotationMatchingPointcut#forClassAnnotation通过注解Class构造Pointcut。ComposablePointcut可以组合多个Pointcut,并进行各种交集、并集操作。这里ShardingTransactionTypeAdvisor的实现是取ShardingTransactionType注解所在类和方法的并集构成切点。

此外,ShardingTransactionTypeAdvisor直接使用ShardingTransactionTypeInterceptor作为Advice的实现。

对于整个Advisor,Order设置为Ordered.LOWEST_PRECEDENCE-1,这是为什么呢?

注意到Spring注解式事务自动配置中,ProxyTransactionManagementConfiguration#transactionAdvisor事务切面如下。

@Bean(name = {"org.springframework.transaction.config.internalTransactionAdvisor"})
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
    BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
    advisor.setTransactionAttributeSource(this.transactionAttributeSource());
    advisor.setAdvice(this.transactionInterceptor());
    advisor.setOrder((Integer)this.enableTx.getNumber("order"));
    return advisor;
}

这个this.enableTx.getNumber("order")取得实际上是EnableTransactionManagement注解的order属性,默认是Ordered.LOWEST_PRECEDENCE。配置ShardingTransactionTypeAdvisor的orderOrdered.LOWEST_PRECEDENCE-1为Ordered.LOWEST_PRECEDENCE-1,这样TransactionTypeHolder.set(TransactionType.XA)设置全局事务类型的方法执行就会在SpringBoot自动配置的事务之前。

public @interface EnableTransactionManagement {
	int order() default Ordered.LOWEST_PRECEDENCE;
}

3、BeanPostProcessor

SpringAOP代理生成的入口是在BeanPostProcessor#postProcessAfterInitialization阶段,AbstractAutoProxyCreator#postProcessAfterInitialization会先执行,创建代理对象,且代理对象实现了Advised接口。

public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
          return wrapIfNecessary(bean, beanName, cacheKey);
      }
  }
  return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // ...

    // Advice & Advisor
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 创建代理,代理持有advisor链
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    // ...
}

ShardingTransactionTypeScanner继承了AbstractAdvisingBeanPostProcessor,在AbstractAutoProxyCreator#postProcessAfterInitialization之后执行,仅仅提供了一个advisor成员变量,就可以实现将ShardingTransactionTypeAdvisor加入Advisor拦截列表。

public final class ShardingTransactionTypeScanner extends AbstractAdvisingBeanPostProcessor {
    
    public ShardingTransactionTypeScanner() {
        setBeforeExistingAdvisors(true);
        this.advisor = new ShardingTransactionTypeAdvisor();
    }
}

AbstractAdvisingBeanPostProcessor的postProcessAfterInitialization方法将ShardingTransactionTypeAdvisor加入了Advisor拦截列表。

public abstract class AbstractAdvisingBeanPostProcessor extends ProxyProcessorSupport implements BeanPostProcessor {

	protected Advisor advisor;
    
    public Object postProcessAfterInitialization(Object bean, String beanName) {
      // ...
      if (bean instanceof Advised) {
          Advised advised = (Advised)bean;
          if (!advised.isFrozen() && this.isEligible(AopUtils.getTargetClass(bean))) {
              if (this.beforeExistingAdvisors) {
                  advised.addAdvisor(0, this.advisor);
              } else {
                  advised.addAdvisor(this.advisor);
              }

              return bean;
          }
      }
      // ...
	}
}

总结

  • ShardingJDBC支持三种事务类型:LOCAL(本地)、XA(强一致两阶段提交)、BASE(最终一致Seata实现)
  • ShardingTransactionManagerEngine事务管理引擎,负责创建事务管理器和事务管理器的初始化,并对外提供获取事务管理器的方法。
  • ShardingTransactionManager事务管理器,XAShardingTransactionManager负责XA事务管理实现,SeataATShardingTransactionManager负责BASE事务管理实现。
  • 本地事务通过WrapperAdapter记录connection的setAutocommit方法调用,待创建实际连接后重放方法调用。最终在用户调用ShardingConnection的commit方法时,循环所有实际Connection执行commit方法。
  • TransactionTypeHolder通过ThreadLocal操作当前线程的全局事务类型。设置全局事务类型,可以通过编码方式(TransactionTypeHolder.set),也可以通过注解+AOP的方式(@ShardingTransactionType+ShardingTransactionTypeAdvisor)。
  • ShardingJDBC利用ShardingTransactionTypeScanner+ShardingTransactionTypeAdvisor+ShardingTransactionTypeInterceptor实现了通过注解设置全局事务类型。