Fescar - RM Proxy创建过程

简介: 开篇 这篇文章的主要是目的是解释清楚DataSourceProxy、ConnectionProxy、StatementProxy几个Proxy代理各自的实现以及相互之间的联系。 希望通过这篇文章,能够解释清楚一个核心问题,就是上述的三个Proxy是如何实现代理并同时保证和原有的JDBC的数据访问逻辑保持不变。

开篇

 这篇文章的主要是目的是解释清楚DataSourceProxy、ConnectionProxy、StatementProxy几个Proxy代理各自的实现以及相互之间的联系。

 希望通过这篇文章,能够解释清楚一个核心问题,就是上述的三个Proxy是如何实现代理并同时保证和原有的JDBC的数据访问逻辑保持不变。

 当然,我个人觉得只有理清楚了三个代理的实现机制以后,才能更好的理解RM的工作原理。


JDBC使用方式

public class Demo {
        // 定义数据库的用户名
        private static final String USERNAME = "root";
        // 定义数据库的密码
        private static final String PASSWORD = "123456";
        // 定义数据库的驱动信息
        private static final String DRIVER = "com.mysql.jdbc.Driver";
        // 定义访问数据库的地址
        private static final String URL = "jdbc:mysql://localhost:3306/mydb";
 
        // 定义访问数据库的连接
        private static Connection connection;
        // 定义sql语句的执行对象
        private static PreparedStatement pstmt;
        // 定义查询返回的结果集合
        private static ResultSet resultSet;     
         
        public static void main(String[] args) throws SQLException {
             
             
            try {
                Class.forName(DRIVER);

                connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);

                //用事务,必须设置setAutoCommit false,表示手动提交
                connection.setAutoCommit(false);

                //设置事务的隔离级别。
                connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
                 
                String sql1 = "insert into userinfo(username,pswd) values(?,?)";
                String sql2 = "update userinfo set pswd=? where username = ?";
                pstmt = connection.prepareStatement(sql1);
                pstmt.setString(1, "CAROL");
                pstmt.setString(2, "123");
                pstmt.executeUpdate();
                 
                pstmt = connection.prepareStatement(sql2);
                pstmt.setString(1, "123456");
                pstmt.setString(2, "nicole");               
                pstmt.executeUpdate();
                //提交事务
                connection.commit();
            } catch (Exception e) {
                // 若事务执行有异常,则事务回滚
                connection.rollback();
          }
}

说明:

  • JDBC的使用方式按照获取驱动、获取连接、创建回话、执行操作的顺序执行。
  • RM Proxy的执行原理和JDBC的使用方式基本一致。
  • JDBC的事务操作也在上述的例子当中,事务在Connection维度。


    <bean name="accountDataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.account.url}"/>
        <property name="username" value="${jdbc.account.username}"/>
        <property name="password" value="${jdbc.account.password}"/>
        <property name="driverClassName" value="${jdbc.account.driver}"/>
        <property name="initialSize" value="0" />
        <property name="maxActive" value="180" />
        <property name="minIdle" value="0" />
        <property name="maxWait" value="60000" />
        <property name="validationQuery" value="Select 'x' from DUAL" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />
        <property name="testWhileIdle" value="true" />
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <property name="minEvictableIdleTimeMillis" value="25200000" />
        <property name="removeAbandoned" value="true" />
        <property name="removeAbandonedTimeout" value="1800" />
        <property name="logAbandoned" value="true" />
        <property name="filters" value="mergeStat" />
    </bean>

    <bean id="accountDataSourceProxy" class="com.alibaba.fescar.rm.datasource.DataSourceProxy">
        <constructor-arg ref="accountDataSource" />
    </bean>

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

说明:

  • 上述配置来自example例子中的dubbo-account-service.xml文件。
  • 上述核心在于创建了DruidDataSource对象accountDataSource。
  • accountDataSource的作用和JDBC中的DriverManager相同。
  • 从DataSource中获取Connection对象,从Connection对象获取Statement对象。


DataSourceProxy

DataSourceProxy.png

说明:

  • DataSource是与数据源对象代表物理数据源连接的工厂,接口由驱动程序供应商实现。
  • 系统API接口DataSource的介绍参考javax.sql.DataSource之api学习。
  • 抽象类AbstractDataSourceProxy实现了DataSource的大部分接口(不包括Connection操作相关接口)。
  • DataSourceProxy实现了核心的接口getConnection实现对于返回Connection代理包装。


DataSourceProxy源码介绍

public abstract class AbstractDataSourceProxy implements DataSource {

    protected DruidDataSource targetDataSource;

    public AbstractDataSourceProxy(DruidDataSource targetDataSource) {
        this.targetDataSource = targetDataSource;
    }

    public DruidDataSource getTargetDataSource() {
        return targetDataSource;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return targetDataSource.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return targetDataSource.isWrapperFor(iface);
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return targetDataSource.getLogWriter();
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {
        targetDataSource.setLogWriter(out);
    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {
        targetDataSource.setLoginTimeout(seconds);
    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return targetDataSource.getLoginTimeout();
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return targetDataSource.getParentLogger();
    }
}
public class DataSourceProxy extends AbstractDataSourceProxy implements Resource {

    private String resourceGroupId = "DEFAULT";

    private boolean managed = false;
    
    // 通过构造函数实现代理对象创建
    public DataSourceProxy(DruidDataSource targetDataSource) {
        super(targetDataSource);
    }

    public DataSourceProxy(DruidDataSource targetDataSource, String resourceGroupId) {
        super(targetDataSource);
        this.resourceGroupId = resourceGroupId;
    }

    private void assertManaged() {
        if (!managed) {
            DataSourceManager.get().registerResource(this);
            managed = true;
        }
    }

    public Connection getPlainConnection() throws SQLException {
        return targetDataSource.getConnection();
    }

    public String getDbType() {
        return targetDataSource.getDbType();
    }

    @Override
    public ConnectionProxy getConnection() throws SQLException {
        assertManaged();
        Connection targetConnection = targetDataSource.getConnection();
        return new ConnectionProxy(this, targetConnection, targetDataSource.getDbType());
    }

    @Override
    public ConnectionProxy getConnection(String username, String password) throws SQLException {
        assertManaged();
        Connection targetConnection = targetDataSource.getConnection(username, password);
        return new ConnectionProxy(this, targetConnection, targetDataSource.getDbType());
    }

    @Override
    public String getResourceGroupId() {
        return resourceGroupId;
    }

    @Override
    public String getResourceId() {
        return targetDataSource.getUrl();
    }
}

说明:

  • AbstractDataSourceProxy实现了DataSource的接口
  • DataSourceProxy继承自AbstractDataSourceProxy,重写了getConnection的方法。
  • DataSourceProxy的构造函数当中传入DruidDataSource targetDataSource。
  • getConnection通过DataSource对象获取连接然后包装成ConnectionProxy对象返回。
  • Connection targetConnection = targetDataSource.getConnection(username, password)。
  • return new ConnectionProxy(this, targetConnection, targetDataSource.getDbType())。
  • DataSourceProxy返回ConnectionProxy对象。


ConnectionProxy

ConnectionProxy.png

说明:

  • ConnectionProxy继承自AbstractConnectionProxy,AbstractConnectionProxy实现Connection接口。
  • AbstractConnectionProxy实现了Connection的大部分接口(不包括commit操作相关接口)。
  • ConnectionProxy实现了Connection的部分接口(commit相关操作的接口)。


ConnectionProxy源码介绍

public abstract class AbstractConnectionProxy implements Connection {

    protected DataSourceProxy dataSourceProxy;
    protected Connection targetConnection;
    protected String dbType;

    public AbstractConnectionProxy(DataSourceProxy dataSourceProxy, Connection targetConnection, String dbType) {
        this.dataSourceProxy = dataSourceProxy;
        this.targetConnection = targetConnection;
        this.dbType = dbType;
    }

    public DataSourceProxy getDataSourceProxy() {
        return dataSourceProxy;
    }

    public Connection getTargetConnection() {
        return targetConnection;
    }

    public String getDbType() {
        return dbType;
    }

    @Override
    public Statement createStatement() throws SQLException {
        Statement targetStatement = getTargetConnection().createStatement();
        return new StatementProxy(this, targetStatement);
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        PreparedStatement targetPreparedStatement = getTargetConnection().prepareStatement(sql);
        return new PreparedStatementProxy(this, targetPreparedStatement, sql);
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        RootContext.assertNotInGlobalTransaction();
        return targetConnection.prepareCall(sql);
    }
}
public class ConnectionProxy extends AbstractConnectionProxy {

    private ConnectionContext context = new ConnectionContext();

    public ConnectionProxy(DataSourceProxy dataSourceProxy, Connection targetConnection, String dbType) {
        super(dataSourceProxy, targetConnection, dbType);
    }

    @Override
    public void commit() throws SQLException {
        if (context.inGlobalTransaction()) {
            try {
                register();
            } catch (TransactionException e) {
                recognizeLockKeyConflictException(e);
            }

            try {
                if (context.hasUndoLog()) {
                    UndoLogManager.flushUndoLogs(this);
                }
                targetConnection.commit();
            } catch (Throwable ex) {
                report(false);
                if (ex instanceof SQLException) {
                    throw (SQLException) ex;
                } else {
                    throw new SQLException(ex);
                }
            }
            report(true);
            context.reset();

        } else {
            targetConnection.commit();
        }
    }

    private void register() throws TransactionException {
        Long branchId = DataSourceManager.get().branchRegister(BranchType.AT, getDataSourceProxy().getResourceId(),
            null, context.getXid(), context.buildLockKeys());
        context.setBranchId(branchId);
    }

    @Override
    public void rollback() throws SQLException {
        targetConnection.rollback();
        if (context.inGlobalTransaction()) {
            if (context.isBranchRegistered()) {
                report(false);
            }}
        context.reset();
    }

}

说明:

  • AbstractConnectionProxy类包含DataSourceProxy和targetConnection两个核心变量。
  • ConnectionProxy继承AbstractConnectionProxy类,ConnectionProxy的构造函数初始化核心变量。
  • AbstractConnectionProxy重写createStatement和prepareStatement方法。
  • createStatement()创建StatementProxy对象, prepareStatement()创建PreparedStatementProxy对象。
  • ConnectionProxy内部实现了Statement对象代理。


StatementProxy

StatementProxy.png
说明:

  • StatementProxy继承自AbstractStatementProxy类,AbstractStatementProxy实现Statement接口。
  • AbstractStatementProxy实现了Statement的大部分接口不包括executeQuery相关的接口。
  • StatementProxy实现了Statement的execute相关的接口。


StatementProxy源码介绍

public abstract class AbstractStatementProxy<T extends Statement> implements Statement {

    protected AbstractConnectionProxy connectionProxy;
    protected T targetStatement;
    protected String targetSQL;

    public AbstractStatementProxy(AbstractConnectionProxy connectionProxy, T targetStatement, String targetSQL) 
     throws SQLException {
        this.connectionProxy = connectionProxy;
        this.targetStatement = targetStatement;
        this.targetSQL = targetSQL;
    }

    public AbstractStatementProxy(ConnectionProxy connectionProxy, T targetStatement) 
     throws SQLException {
        this(connectionProxy, targetStatement, null);
    }

    public AbstractConnectionProxy getConnectionProxy() {
        return connectionProxy;
    }

    public T getTargetStatement() {
        return targetStatement;
    }

    public String getTargetSQL() {
        return targetSQL;
    }
}
public class StatementProxy<T extends Statement> extends AbstractStatementProxy<T> {

    public StatementProxy(AbstractConnectionProxy connectionWrapper, T targetStatement, String targetSQL) 
     throws SQLException {
        super(connectionWrapper, targetStatement, targetSQL);
    }

    public StatementProxy(AbstractConnectionProxy connectionWrapper, T targetStatement) 
     throws SQLException {
        this(connectionWrapper, targetStatement, null);
    }

    @Override
    public ConnectionProxy getConnectionProxy() {
        return (ConnectionProxy) super.getConnectionProxy();
    }

    @Override
    public ResultSet executeQuery(String sql) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<ResultSet, T>() {
            @Override
            public ResultSet execute(Statement statement, Object... args) throws SQLException {
                return statement.executeQuery((String) args[0]);
            }
        }, sql);
    }

    @Override
    public int executeUpdate(String sql) throws SQLException {
        this.targetSQL = sql;
        return ExecuteTemplate.execute(this, new StatementCallback<Integer, T>() {
            @Override
            public Integer execute(Statement statement, Object... args) throws SQLException {
                return statement.executeUpdate((String) args[0]);
            }
        }, sql);
    }

    @Override
    public boolean execute(String sql) throws SQLException {
        // TODO
        return false;
    }
}

说明:

  • AbstractStatementProxy包含connectionProxy和targetStatement核心变量。
  • StatementProxy继承AbstractStatementProxy类,构造函数中初始化核心的两个变量。
  • StatementProxy实现核心的executeUpdate和executeQuery操作。


期待

 下一篇文章尝试讲解ExecuteTemplate这个核心操作,然后整个执行链路就串联起来了,有了完整的执行链路,分析RM的执行过程也就水到渠成了。

目录
相关文章
|
IDE 程序员 Go
JupyterLab教程:程序员的笔记本神器v2.0
JupyterLab教程:程序员的笔记本神器v2.0
JupyterLab教程:程序员的笔记本神器v2.0
|
Java 测试技术 Android开发
Java 测试和调试:提高代码质量的实用策略
【4月更文挑战第27天】测试和调试是软件开发中确保应用稳定、高效且可靠的关键步骤。对于 Java 开发者来说,掌握有效的测试和调试技巧可以大大提高代码质量和减少生产环境下的问题。
309 2
|
JSON 前端开发 定位技术
Echarts地图坐标geoCoordMap后台生成动态获取的解决方案
Echarts地图坐标geoCoordMap后台生成动态获取的解决方案
426 0
|
6天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
6229 8
|
4天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
1174 3
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
5天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
713 4
|
18天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
3341 10
|
17天前
|
IDE 开发工具
Qoder 上线 Sonus 模型,Computer Use 能力全面增强
Qoder国际版上线全新内置大模型Sonus(/ˈsoʊnəs/),全球领先,专精超长任务执行与电脑操作(Computer Use)。配合Qoder桌面端0.2.3版本,可自主完成编程、金融建模、科研及表格制作等复杂工作。现全面支持Qoder全系产品,效率提升3.2倍。
1855 8
Qoder 上线 Sonus 模型,Computer Use 能力全面增强

热门文章

最新文章