美文网首页征服Spring程序员Java学习笔记
Spring框架JdbcTemplate所用的命令模式之我见

Spring框架JdbcTemplate所用的命令模式之我见

作者: 马拉松Mara | 来源:发表于2017-07-19 11:29 被阅读381次

    概述

    最近回顾了一下设计模式。想到Spring框架中,使用设计模式挺多的。于是搜索了一下Spring中有没有使用命令模式?
    参照:命令模式浅析,然后对Spring中的JdbcTemplate类进行了源码阅读,现在就命令模式,对JdbcTemplate中的部分代码做一下解读。

    命令模式简介

    在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,
    我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,
    使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。

    举个例子吧,将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
    Invoker是调用者(将军),Receiver是被调用者(士兵),MyCommand是命令,实现了Command接口,持有接收对象

    JdbcTemplate部分代码解析

    此类在工作中经常被使用。其中的query方法,进行了大量的重载。
    我们拿其中的一个重载方法来说:

        @Override
        public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
            return query(sql, new RowMapperResultSetExtractor<T>(rowMapper));
        }
    

    这个方法调用了:

    @Override
        public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
            Assert.notNull(sql, "SQL must not be null");
            Assert.notNull(rse, "ResultSetExtractor must not be null");
            if (logger.isDebugEnabled()) {
                logger.debug("Executing SQL query [" + sql + "]");
            }
            class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
                @Override
                public T doInStatement(Statement stmt) throws SQLException {
                    ResultSet rs = null;
                    try {
                        rs = stmt.executeQuery(sql);
                        ResultSet rsToUse = rs;
                        if (nativeJdbcExtractor != null) {
                            rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
                        }
                        return rse.extractData(rsToUse);
                    }
                    finally {
                        JdbcUtils.closeResultSet(rs);
                    }
                }
                @Override
                public String getSql() {
                    return sql;
                }
            }
            return execute(new QueryStatementCallback());
        }
    

    其中,我们发现有一个匿名内部类:** QueryStatementCallback,它实现了 StatementCallback接口。
    StatementCallback接口中有唯一的
    doInStatement**方法:

    T doInStatement(Statement stmt) throws SQLException, DataAccessException;
    

    我们再接着往下看,

    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException
    

    最后,调用了execute(new QueryStatementCallback())方法,并且把匿名内部类QueryStatementCallback的实例对象当做参数传递了过去。

    @Override
        public <T> T execute(StatementCallback<T> action) throws DataAccessException {
            Assert.notNull(action, "Callback object must not be null");
    
            Connection con = DataSourceUtils.getConnection(getDataSource());
            Statement stmt = null;
            try {
                Connection conToUse = con;
                if (this.nativeJdbcExtractor != null &&
                        this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) {
                    conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
                }
                stmt = conToUse.createStatement();
                applyStatementSettings(stmt);
                Statement stmtToUse = stmt;
                if (this.nativeJdbcExtractor != null) {
                    stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
                }
                T result = action.doInStatement(stmtToUse);
                handleWarnings(stmt);
                return result;
            }
            catch (SQLException ex) {
                // Release Connection early, to avoid potential connection pool deadlock
                // in the case when the exception translator hasn't been initialized yet.
                JdbcUtils.closeStatement(stmt);
                stmt = null;
                DataSourceUtils.releaseConnection(con, getDataSource());
                con = null;
                throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex);
            }
            finally {
                JdbcUtils.closeStatement(stmt);
                DataSourceUtils.releaseConnection(con, getDataSource());
            }
        }
    

    我们着重看一下这一行代码:

    T result = action.doInStatement(stmtToUse);
    

    其中action参数是** StatementCallback**类型。

    命令模式角色对应解析

    在这个query查询中,我们可以把 ** StatementCallback接口看做命令接口
    匿名内部类
    QueryStatementCallback是该命令接口的一个具体实现命令。
    QueryStatementCallback中,对 doInStatement接口进行了重写,具体实现了命令的执行。(相当于执行命令的士兵,这里没有用具体的类去单独写)
    而命令调用者(将军),是
    T execute(StatementCallback<T> action)方法。根据传递的具体命令不同,最后action.doInStatement(stmtToUse)执行的具体命令也就不同。
    其中,
    QueryStatementCallback的具体实现类还有以下几个:

    image.png
    同时,我们可以看到命令调用者:
    T execute(StatementCallback<T> action)**方法调用时,也对应传递了相应的具体命令。 image.png

    备注

    • Spring版本:4.3

    相关文章

      网友评论

        本文标题:Spring框架JdbcTemplate所用的命令模式之我见

        本文链接:https://www.haomeiwen.com/subject/fuflkxtx.html