ShardingSphere源码解析之执行引擎(四)

上一篇中,我们详细讨论了ShardingConnection对象。我们知道ShardingSphere的设计目标是实现一套与JDBC规范完全兼容的框架体系,因此对JDBC规范的重写是关键。我们知道JDBC规范中的几个重要概念包括DataSource、Connection、Statement、PreparedStament。而ShardingSphere通过适配器(Adapter)设计模式包装了自己的实现类,如ShardingDataSource、ShardingConnection、ShardingStatement、ShardingPreparedStament。在org.apache.shardingsphere.shardingjdbc.jdbc.adapter包中包含了所有与Adapter相关的实现类:

我们继续以上一篇中介绍的ShardingConnection为例来介绍Adapter模式的具体应用,这是ShardingConnection的另一条类层结构支线,即作为一种Connection对象所应该具备的基本结构,如下图所示:

我们首先来看AbstractConnectionAdapter,ShardingConnection直接继承了它。我们在AbstractConnectionAdapter中发现了一个cachedConnections属性,它是一个Map对象,该对象其实缓存了这个经过封装的ShardingConnection背后真实的Connection对象。如果我们对一个AbstractConnectionAdapter重复使用,那么这些cachedConnections也会一直被缓存,直到调用close方法。可以从AbstractConnectionAdapter的getConnections方法中理解具体的操作过程,如下所示:

public final List<Connection> getConnections(final ConnectionMode connectionMode, final String dataSourceName, final int connectionSize) throws SQLException {

        //获取DataSource

    DataSource dataSource = getDataSourceMap().get(dataSourceName);

        Preconditions.checkState(null != dataSource, "Missing the data source name: '%s'", dataSourceName);

        Collection<Connection> connections;       

        //根据数据源从cachedConnections中获取connections

        synchronized (cachedConnections) {

            connections = cachedConnections.get(dataSourceName);

        }       

        //如果connections多于想要的connectionSize,则只获取所需部分

        List<Connection> result;

        if (connections.size() >= connectionSize) {

            result = new ArrayList<>(connections).subList(0, connectionSize);

        } else if (!connections.isEmpty()) {//如果connections不够

            result = new ArrayList<>(connectionSize);

            result.addAll(connections);

            //创建新的connections

            List<Connection> newConnections = createConnections(dataSourceName, connectionMode, dataSource, connectionSize - connections.size());

            result.addAll(newConnections);

            synchronized (cachedConnections) {

              //将新创建的connections也放入缓存中进行管理

                cachedConnections.putAll(dataSourceName, newConnections);

            }

        } else {//如果缓存中没有对应dataSourceConnections,同样进行创建并放入缓存中

            result = new ArrayList<>(createConnections(dataSourceName, connectionMode, dataSource, connectionSize));

            synchronized (cachedConnections) {

                cachedConnections.putAll(dataSourceName, result);

            }

        }

        return result;

    }

这段代码有三个判断,流程上比较简单,参考注释即可。这里需要关注的是其中的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);

            replayMethodsInvocation(connection);

            return Collections.singletonList(connection);

        }

        if (ConnectionMode.CONNECTION_STRICTLY == connectionMode) {

            return createConnections(dataSourceName, dataSource, connectionSize);

        }

        synchronized (dataSource) {

            return createConnections(dataSourceName, dataSource, connectionSize);

        }

    }

这里调用了抽象方法createConnection(另一个createConnections方法就是批量调用了createConnection方法),该方法需要AbstractConnectionAdapter的子类进行实现:

protected abstract Connection createConnection(String dataSourceName, DataSource dataSource) throws SQLException;   

同时,我们看到对于创建的Connection对象,都需要执行如下语句:

replayMethodsInvocation(connection);

这句话比较难以理解,让我们来到定义它的地方,即WrapperAdapter类。从命名上看,它是一个包装器的适配类,实现了JDBC中的Wrapper接口。我们在该类中找到了如下所示的一对方法定义:

    public final void recordMethodInvocation(final Class<?> targetClass, final String methodName, final Class<?>[] argumentTypes, final Object[] arguments) {

        jdbcMethodInvocations.add(new JdbcMethodInvocation(targetClass.getMethod(methodName, argumentTypes), arguments));

    }   

    public final void replayMethodsInvocation(final Object target) {

        for (JdbcMethodInvocation each : jdbcMethodInvocations) {

            each.invoke(target);

        }

    }

这两个方法都用到了JdbcMethodInvocation类,它的定义如下所示:

public class JdbcMethodInvocation {   

    @Getter

    private final Method method;   

    @Getter

    private final Object[] arguments;   

    public void invoke(final Object target) {

        method.invoke(target, arguments);

    }

}

显然,这里用到了反射技术根据传入的method和arguments对象执行对应方法。了解了JdbcMethodInvocation类的原理之后,我们就不难理解recordMethodInvocation和replayMethodsInvocation方法的作用。其中,recordMethodInvocation用于记录需要执行的方法和参数,而replayMethodsInvocation则根据这些方法和参数通过反射技术进行执行。

对于执行replayMethodsInvocation,我们必须先找到recordMethodInvocation的调用入口。通过代码的调用关系,我们看到在AbstractConnectionAdapter和AbstractStatementAdapter中集中对其进行了调用:

通过上图,我们知道在AbstractConnectionAdapter中的setAutoCommit、setReadOnly和setTransactionIsolation这三个方法中存在对recordMethodInvocation的调用,比方说如下所示的setReadOnly中的调用:

//调用recordMethodInvocation方法记录方法调用的元数据

recordMethodInvocation(Connection.class, "setReadOnly", new Class[]{boolean.class}, new Object[]{readOnly});

当完成各种recordMethodInvocation方法的调用之后,我们就可以通过在新创建的Connection对象上执行replayMethodsInvocation方法,该方法会遍历所有通过recordMethodInvocation传入的方法和参数并依次进行执行,也就是说相当于执行了如上所示的setReadOnly等方法。这样做的好处是我们可以先把相应的操作存储起来,等真正有Connection对象的时候再进行方法的调用,设计上非常巧妙。

然后,我们再以setReadOnly方法为例,来看它的完整实现,如下所示:

    @Override

    public final void setReadOnly(final boolean readOnly) throws SQLException {

        this.readOnly = readOnly;

       //调用recordMethodInvocation方法记录方法调用的元数据

        recordMethodInvocation(Connection.class, "setReadOnly", new Class[]{boolean.class}, new Object[]{readOnly});

        forceExecuteTemplate.execute(cachedConnections.values(), new ForceExecuteCallback<Connection>() {           

            @Override

            public void execute(final Connection connection) throws SQLException {

                connection.setReadOnly(readOnly);

            }

        });

    }       

这里还看到了一个新的模板类,即ForceExecuteTemplate,该类与ForceExecuteCallback是一对。它们的作用也是通过回调的方法来执行定制化的逻辑,在这个场景中,就是强制对cachedConnections中所有缓存的Connection执行setReadOnly方法。

另一方面,从类层关系上,我们看到AbstractConnectionAdapter直接继承的是AbstractUnsupportedOperationConnection而不是WrapperAdapter,而在AbstractUnsupportedOperationConnection中都是一批直接抛出异常的方法,这样做的目的就是明确哪些操作是AbstractConnectionAdapter及其子类ShardingConnection所不能支持的,属于职责分离的一种具体实现方法。

更多内容可以关注我的公众号:程序员向架构师转型。

发布了120 篇原创文章 · 获赞 14 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/lantian08251/article/details/104894157
今日推荐