Spring中Template模式與callback的結合使用淺析

Spring不管是與ibatis,仍是與Hibernate的結合中,都使用到了Template模式與callback技術,來達到簡化代碼實現的目的。Template模式也即模板模式,用於對一些不太變化的流程進行模板化,與callback結合,能夠將變化的部分出離出來,使用callback實現。而後根據不一樣的狀況,向template注入不一樣的callback。那些模板代碼就沒有必要重複寫了。咱們看下spring和ibatis的結合中,Template和callback的使用:spring

public class SqlMapClientTemplate extends JdbcAccessor implements SqlMapClientOperations {
    // ... ...
    /**
     * Execute the given data access action on a SqlMapExecutor.
     * @param action callback object that specifies the data access action
     * @return a result object returned by the action, or <code>null</code>
     * @throws DataAccessException in case of SQL Maps errors
     */
    public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");
        Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");

        // We always need to use a SqlMapSession, as we need to pass a Spring-managed
        // Connection (potentially transactional) in. This shouldn't be necessary if
        // we run against a TransactionAwareDataSourceProxy underneath, but unfortunately
        // we still need it to make iBATIS batch execution work properly: If iBATIS
        // doesn't recognize an existing transaction, it automatically executes the
        // batch for every single statement...

        SqlMapSession session = this.sqlMapClient.openSession();
        if (logger.isDebugEnabled()) {
            logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");
        }
        Connection ibatisCon = null;

        try {
            Connection springCon = null;
            DataSource dataSource = getDataSource();
            boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);

            // Obtain JDBC Connection to operate on...
            try {
                ibatisCon = session.getCurrentConnection();
                if (ibatisCon == null) {
                    springCon = (transactionAware ?
                            dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));
                    session.setUserConnection(springCon);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");
                    }
                }
                else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");
                    }
                }
            }
            catch (SQLException ex) {
                throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
            }

            // Execute given callback...
            try {
                return action.doInSqlMapClient(session);
            }
            catch (SQLException ex) {
                throw getExceptionTranslator().translate("SqlMapClient operation", null, ex);
            }
            finally {
                try {
                    if (springCon != null) {
                        if (transactionAware) {
                            springCon.close();
                        }
                        else {
                            DataSourceUtils.doReleaseConnection(springCon, dataSource);
                        }
                    }
                }
                catch (Throwable ex) {
                    logger.debug("Could not close JDBC Connection", ex);
                }
            }

            // Processing finished - potentially session still to be closed.
        }
        finally {
            // Only close SqlMapSession if we know we've actually opened it
            // at the present level.
            if (ibatisCon == null) {
                session.close();
            }
        }
    }
 
public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException
該方法就是一個模板方法,方法的參數是一個回調對象。在模板方法中,將一些相同的處理過程模板化,好比得到數據庫鏈接,處理事務,處理異常,關閉資源等等,
這些都是每個sql執行時都要面臨的相同的過程,因此咱們將他們房子模板方法中。而後將不一樣的部分經過 callback 對象做爲參數傳入進去,這樣將模板代碼和非
模板代碼進行了隔離。沒有必要將模板代碼每次都寫一遍。

 

    public Object queryForObject(final String statementName, final Object parameterObject)
            throws DataAccessException {

        return execute(new SqlMapClientCallback<Object>() {
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
                return executor.queryForObject(statementName, parameterObject);
            }
        });
    }

    public List queryForList(final String statementName, final Object parameterObject)
            throws DataAccessException {

        return execute(new SqlMapClientCallback<List>() {
            public List doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
                return executor.queryForList(statementName, parameterObject);
            }
        });
    }

    public Map queryForMap(
            final String statementName, final Object parameterObject, final String keyProperty)
            throws DataAccessException {

        return execute(new SqlMapClientCallback<Map>() {
            public Map doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
                return executor.queryForMap(statementName, parameterObject, keyProperty);
            }
        });
    }

 

咱們看一下上面這些方法,都是藉助模板方法來處理那些每次都相同的流程,而後傳入一個本身實現的 callback 對象。模板會自動回調咱們在 callback 對象中定義的方法。sql

咱們看下JdbcTemplate中的模板方法也是類似的:數據庫

    public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
        Assert.notNull(action, "Callback object must not be null");

        Connection con = DataSourceUtils.getConnection(getDataSource());
        try {
            Connection conToUse = con;
            if (this.nativeJdbcExtractor != null) {
                // Extract native JDBC Connection, castable to OracleConnection or the like.
                conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
            }
            else {
                // Create close-suppressing Connection proxy, also preparing returned Statements.
                conToUse = createConnectionProxy(con);
            }
            return action.doInConnection(conToUse);
        }
        catch (SQLException ex) {
            // Release Connection early, to avoid potential connection pool deadlock
            // in the case when the exception translator hasn't been initialized yet.
            DataSourceUtils.releaseConnection(con, getDataSource());
            con = null;
            throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
        }
        finally {
            DataSourceUtils.releaseConnection(con, getDataSource());
        }
    }
相關文章
相關標籤/搜索