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所不能支持的,屬於職責分離的一種具體實現方法。

更多內容可以關注我的公衆號:程序員向架構師轉型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章