seata数据源代理流程

1-SeataDataSourceAutoConfiguration

创建SeataAutoDataSourceProxyCreator对象,默认seata模式为AT

2-SeataAutoDataSourceProxyCreator

  • 设置advisor用于来接实现DataSource接口的实现类
    public SeataAutoDataSourceProxyCreator(boolean useJdkProxy, String[] excludes, String dataSourceProxyMode) {
    setProxyTargetClass(!useJdkProxy);
    this.excludes = new HashSet<>(Arrays.asList(excludes));
    // AT
    this.dataSourceProxyMode = dataSourceProxyMode;
    this.advisors = buildAdvisors(dataSourceProxyMode);
    } private Object[] buildAdvisors(String dataSourceProxyMode) {
    Advice advice = new SeataAutoDataSourceProxyAdvice(dataSourceProxyMode);
    return new Object[]{new DefaultIntroductionAdvisor(advice)};
    }
  • 重写wrapIfNecessary,设置数据源代理
      @Override
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // we only care DataSource bean
    if (!(bean instanceof DataSource)) {
    return bean;
    } // when this bean is just a simple DataSource, not SeataDataSourceProxy
    LOGGER.error("bean={}", bean.getClass().getSimpleName());
    if (!(bean instanceof SeataDataSourceProxy)) {
    Object enhancer = super.wrapIfNecessary(bean, beanName, cacheKey);
    // this mean this bean is either excluded by user or had been proxy before
    if (bean == enhancer) {
    return bean;
    }
    // else, build proxy, put <origin, proxy> to holder and return enhancer
    DataSource origin = (DataSource) bean;
    SeataDataSourceProxy proxy = buildProxy(origin, dataSourceProxyMode);
    DataSourceProxyHolder.put(origin, proxy);
    LOGGER.error("bean2origin={}, proxy={}, proxy.targetDataSource={}", origin.getClass().getSimpleName(), proxy.getClass().getSimpleName(), proxy.getTargetDataSource());
    return enhancer;
    } /*
    * things get dangerous when you try to register SeataDataSourceProxy bean by yourself!
    * if you insist on doing so, you must make sure your method return type is DataSource,
    * because this processor will never return any subclass of SeataDataSourceProxy
    */
    LOGGER.warn("Manually register SeataDataSourceProxy(or its subclass) bean is discouraged! bean name: {}", beanName);
    SeataDataSourceProxy proxy = (SeataDataSourceProxy) bean;
    DataSource origin = proxy.getTargetDataSource();
    LOGGER.error("TargetDataSource={}", origin.getClass().getSimpleName());
    Object originEnhancer = super.wrapIfNecessary(origin, beanName, cacheKey);
    // this mean origin is either excluded by user or had been proxy before
    if (origin == originEnhancer) {
    return origin;
    }
    // else, put <origin, proxy> to holder and return originEnhancer
    DataSourceProxyHolder.put(origin, proxy);
    return originEnhancer;
    }

3-SeataAutoDataSourceProxyAdvice

所有实现DataSource接口的实现类都会被拦截而调用invoke方法。

此处真是被调用的方法是DataSourceProxy#getConnection(),该方法会返回connection代理对象ConnectionProxy。

数据库操作初始化流程:DataSource->Connection->PrepareStatement

  @Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// check whether current context is expected
if (!inExpectedContext()) {
return invocation.proceed();
} Method method = invocation.getMethod();
String name = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes(); Method declared;
try {
declared = DataSource.class.getDeclaredMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
// mean this method is not declared by DataSource
return invocation.proceed();
} // switch invoke instance to its proxy
DataSource origin = (DataSource) invocation.getThis();
SeataDataSourceProxy proxy = DataSourceProxyHolder.get(origin);
Object[] args = invocation.getArguments();
LOGGER.error("执行类={},执行方法={},方法参数={}", proxy.getClass().getSimpleName(), declared.getName(), args);
return declared.invoke(proxy, args);
}

4-AbstractConnectionProxy

在上一步操作中,会得到connection代理对象ConnectionProxy,数据库SQL在执行前还需要初始化PreparedStatement,此时会调用ConnectionProxy父类AbstractConnectionProxy#prepareStatement(sql)方法,从而得到PreparedStatement代理类PreparedStatementProxy。

  @Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
String dbType = getDbType();
// support oracle 10.2+
PreparedStatement targetPreparedStatement = null;
if (BranchType.AT == RootContext.getBranchType()) {
List<SQLRecognizer> sqlRecognizers = SQLVisitorFactory.get(sql, dbType);
if (sqlRecognizers != null && sqlRecognizers.size() == 1) {
SQLRecognizer sqlRecognizer = sqlRecognizers.get(0);
if (sqlRecognizer != null && sqlRecognizer.getSQLType() == SQLType.INSERT) {
TableMeta tableMeta = TableMetaCacheFactory.getTableMetaCache(dbType).getTableMeta(getTargetConnection(), sqlRecognizer.getTableName(), getDataSourceProxy().getResourceId());
String[] pkNameArray = new String[tableMeta.getPrimaryKeyOnlyName().size()];
tableMeta.getPrimaryKeyOnlyName().toArray(pkNameArray);
targetPreparedStatement = getTargetConnection().prepareStatement(sql, pkNameArray);
}
}
}
if (targetPreparedStatement == null) {
targetPreparedStatement = getTargetConnection().prepareStatement(sql);
}
return new PreparedStatementProxy(this, targetPreparedStatement, sql);
}

5-PreparedStatementProxy

SQL在执行时会调用PreparedStatementProxy#execute方法

    @Override
public boolean execute() throws SQLException {
// 执行两次
// 第一次:代理类PreparedStatementProxy
// 第二次:被代理的真实的PreparedStatement,即:targetStatement,此时执行业务SQL
return ExecuteTemplate.execute(this, (statement, args) -> statement.execute());
} @Override
public ResultSet executeQuery() throws SQLException {
return ExecuteTemplate.execute(this, (statement, args) -> statement.executeQuery());
} @Override
public int executeUpdate() throws SQLException {
return ExecuteTemplate.execute(this, (statement, args) -> statement.executeUpdate());
}

6-ConnectionProxy

SQL在执行完成后,最后一步会执行commit方法

    @Override
public void commit() throws SQLException {
try {
lockRetryPolicy.execute(() -> {
doCommit();
return null;
});
} catch (SQLException e) {
if (targetConnection != null && !getAutoCommit() && !getContext().isAutoCommitChanged()) {
rollback();
}
throw e;
} catch (Exception e) {
throw new SQLException(e);
}
}

seata数据源代理的更多相关文章

  1. 分布式事务(Seata)原理 详解篇,建议收藏

    前言 在之前的系列中,我们讲解了关于Seata基本介绍和实际应用,今天带来的这篇,就给大家分析一下Seata的源码是如何一步一步实现的.读源码的时候我们需要俯瞰起全貌,不要去扣一个一个的细节,这样我们 ...

  2. seata整合多数据源

    seata整合多数据源 一.背景 二.整合步骤 1.seata server的搭建 2.引入数据源切换组件 3.引入seata组件 4.配置多数据源 5.关闭seata自己默认的数据源代理 6.配置s ...

  3. Spring Cloud同步场景分布式事务怎样做?试试Seata

    一.概述 在微服务架构下,虽然我们会尽量避免分布式事务,但是只要业务复杂的情况下这是一个绕不开的问题,如何保证业务数据一致性呢?本文主要介绍同步场景下使用Seata的AT模式来解决一致性问题. Sea ...

  4. 分布式事务解决方案,中间件 Seata 的设计原理详解

    作者:张乘辉 前言 在微服务架构体系下,我们可以按照业务模块分层设计,单独部署,减轻了服务部署压力,也解耦了业务的耦合,避免了应用逐渐变成一个庞然怪物,从而可以轻松扩展,在某些服务出现故障时也不会影响 ...

  5. 开发者说 | 分布式事务中间件 Seata 的设计原理

    导读 微服务架构体系下,我们可以按照业务模块分层设计,单独部署,减轻了服务部署压力,也解耦了业务的耦合,避免了应用逐渐变成一个庞然怪物,从而可以轻松扩展,在某些服务出现故障时也不会影响其它服务的正常运 ...

  6. 分布式事务框架-seata初识

    一.事务与分布式事务 事务,在数据库中指的是操作数据库的最小单位,往大了看,事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消. 那为什么会有分布式事务呢 ...

  7. 架构设计 | 基于Seata中间件,微服务模式下事务管理

    源码地址:GitHub·点这里 || GitEE·点这里 一.Seata简介 1.Seata组件 Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata将为用 ...

  8. 分析 5种分布式事务方案,还是选了阿里的 Seata(原理 + 实战)

    好长时间没发文了,最近着实是有点忙,当爹的第 43 天,身心疲惫.这又赶上年底,公司冲 KPI 强制技术部加班到十点,晚上孩子隔两三个小时一醒,基本没睡囫囵觉的机会,天天处于迷糊的状态,孩子还时不时起 ...

  9. Spring Cloud Alibaba 初体验(六) Seata 及结合 MyBatis 与 MyBatis-Plus 的使用

    一.下载与运行 本文使用 Seata 1.1.0:https://github.com/seata/seata/releases Windows 环境下双击 bin/seata-server.bat ...

  10. 二、springboot项目使用seata实现分布式事务

    所有文章 https://www.cnblogs.com/lay2017/p/12078232.html 正文 在上一篇文章中,我们简单地了解了一下什么是seata.它是来自阿里巴巴的内部项目不断地发 ...

随机推荐

  1. STM32F4库函数初始化系列:DMA串口接收

    1 void _UART2_Configuration(void) 2 { 3 USART_InitTypeDef USART_InitStructure; 4 5 USART_OverSamplin ...

  2. ChatGPT调研分析与应用场域结合构想

    作者:京东科技 胡骏 摘要 1. ChatGPT调研分析 2022年11月30日,ChatGPT横空出世,在全球范围内形成了热烈的讨论.根据Similarweb的数据,今年1月,平均每天约有1300万 ...

  3. JZOJ 5947.初音未来(miku)

    题目大意 维护一个数列 \(a_n\),\(m\) 次操作,每次对区间 \([l..r]\) 进行升序排序 求最后询问区间 \([L..R]\),输出 \(a_L,a_{L+1},···,a_{R}\ ...

  4. Quartz.Net 主要概念介绍和吐槽

    我们经常遇到需要定时执行某些任务的情况,比如清理缓存.异步结果轮询等,如果不打算造轮子,那么选择一款合适的定时任务组件就很关键了.所幸,.Net 世界中的选项并不多:) 选型 主要有以下四款: Qua ...

  5. 题解 [HAOI2007]分割矩阵

    % 你赛考到了,看到如此之小的数据范围,想到考前每次都被状压 dp 吊起来打的惨痛经历,第一反应就是状压. 然后发现横竖切这个不太好记录就摆了去看 T2,然后没想到这么简单. 令 \(f_{a, b, ...

  6. HACLABS: NO_NAME

    HACLABS: NO_NAME 目录 HACLABS: NO_NAME 1 信息收集 1.1 端口扫描 1.2 后台目录扫描 1.2.1 目录分析 2 命令注入利用 2.1 尝试反弹Shell 2. ...

  7. HACKNOS: RECONFORCE (V1.1)

    HACKNOS: RECONFORCE (V1.1) 目录 HACKNOS: RECONFORCE (V1.1) 1 信息收集 1.1 端口扫描 1.2 ftp分析 1.3 后台目录扫描 1.2.1 ...

  8. Cobalt Strike 之: Aggressor Script

    郑重声明: 本笔记编写目的只用于安全知识提升,并与更多人共享安全知识,切勿使用笔记中的技术进行违法活动,利用笔记中的技术造成的后果与作者本人无关.倡导维护网络安全人人有责,共同维护网络文明和谐. Co ...

  9. 02#Web 实战:实现树形控件

    前言 这是一篇个人练习 Web 前端各种常见的控件.组件的实战系列文章.本篇文章将介绍个人通过 JQuery + 无序列表 + CSS 动画完成一个简易的树形控件. 最终实现的效果是: 这样结构比较复 ...

  10. ImGui引入深度测试的方法

    You can use AddCallback() on a given ImDrawList:: to register functions to be run during rendering a ...