MyBatis设计模式之装饰器、模版方法、策略模式

MyBatis设计模式深度解析(二)

一、MyBatis整体架构与设计模式续篇

在上一篇文章中,我们深入讲解了MyBatis中的构建者模式、工厂模式和代理模式。本文将继续探讨MyBatis中另外三种重要的设计模式:装饰器模式、模板方法模式和策略模式。这些设计模式的应用,使得MyBatis在功能扩展、性能优化和架构灵活性方面表现出色。

1.1 MyBatis整体架构回顾

MyBatis采用分层架构设计,从上到下包括:

  • 应用层:用户应用程序
  • 接口层:SqlSession、Mapper接口
  • 核心处理层:SQL解析、执行、结果映射
  • 基础支撑层:缓存、事务、类型处理、日志等
  • 数据层:数据源、连接池、JDBC驱动

在本文中,我们将重点探讨装饰器模式、模板方法模式和策略模式在这些层次中的应用。

1.2 本文涉及的设计模式

  1. 装饰器模式(Decorator Pattern):CachingExecutor、Cache装饰器链
  2. 模板方法模式(Template Method Pattern):BaseExecutor、BaseTypeHandler
  3. 策略模式(Strategy Pattern):RoutingStatementHandler、不同Executor策略

二、装饰器模式(Decorator Pattern)

装饰器模式是一种结构型设计模式,它允许你在不改变对象结构的情况下,动态地为对象添加新的行为。装饰器模式通过将对象包装在装饰器类中来实现功能的增强。

2.1 CachingExecutor

CachingExecutor是MyBatis中最典型的装饰器模式应用。它为Executor添加了二级缓存功能。

2.1.1 类定义
java 复制代码
public class CachingExecutor implements Executor {

    private final Executor delegate;  // 被装饰的Executor
    private final TransactionalCacheManager tcm = new TransactionalCacheManager();

    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
        // 确保被装饰的Executor类型是SIMPLE、REUSE或BATCH
        if (delegate instanceof CachingExecutor) {
            throw new IllegalArgumentException("Cannot wrap a CachingExecutor");
        }
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 1. 获取绑定SQL
        BoundSql boundSql = ms.getBoundSql(parameterObject);
        // 2. 创建缓存Key
        CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
        // 3. 执行查询(先查缓存,缓存没有则查数据库)
        return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        // 4. 获取MappedStatement的缓存配置
        Cache cache = ms.getCache();

        if (cache != null) {
            // 5. 刷新缓存(如果需要)
            flushCacheIfRequired(ms);
            if (ms.isUseCache() && resultHandler == null) {
                // 6. 确保参数不为null时才使用缓存
                ensureNoOutParams(ms, boundSql);
                // 7. 从事务缓存管理器获取缓存
                @SuppressWarnings("unchecked")
                List<E> list = (List<E>) tcm.getObject(cache, key);
                if (list == null) {
                    // 8. 缓存未命中,委托给被装饰的Executor执行查询
                    list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
                    // 9. 将查询结果放入缓存
                    tcm.putObject(cache, key, list); // issue #578 and #116
                }
                return list;
            }
        }
        // 10. 没有配置缓存,直接委托给被装饰的Executor
        return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
    }

    @Override
    public int update(MappedStatement ms, Object parameterObject) throws SQLException {
        // 更新操作先刷新缓存
        flushCacheIfRequired(ms);
        // 委托给被装饰的Executor执行更新
        return delegate.update(ms, parameterObject);
    }

    private void flushCacheIfRequired(MappedStatement ms) {
        Cache cache = ms.getCache();
        if (cache != null && ms.isFlushCacheRequired()) {
            tcm.clear(cache);
        }
    }
}
2.1.2 装饰器模式的优势
  1. 不修改原始类:通过包装而非继承来扩展功能
  2. 动态组合:可以在运行时动态地添加或删除功能
  3. 职责单一:每个装饰器只负责一个功能
  4. 灵活扩展:可以多层嵌套,实现功能的灵活组合

2.2 Cache装饰器链

MyBatis的缓存系统也大量使用了装饰器模式。Cache接口有多个装饰器实现,形成了一个装饰器链。

2.2.1 Cache接口
java 复制代码
public interface Cache {
    String getId();
    void putObject(Object key, Object value);
    Object getObject(Object key);
    Object removeObject(Object key);
    void clear();
    int getSize();
    ReadWriteLock getReadWriteLock();
}
2.2.2 Cache装饰器实现
java 复制代码
// 1. PerpetualCache - 基础缓存实现
public class PerpetualCache implements Cache {
    private final String id;
    private Map<Object, Object> cache = new HashMap<>();

    public PerpetualCache(String id) {
        this.id = id;
    }

    @Override
    public void putObject(Object key, Object value) {
        cache.put(key, value);
    }

    @Override
    public Object getObject(Object key) {
        return cache.get(key);
    }

    @Override
    public Object removeObject(Object key) {
        return cache.remove(key);
    }

    @Override
    public void clear() {
        cache.clear();
    }
}

// 2. LruCache - LRU淘汰策略装饰器
public class LruCache implements Cache {
    private final Cache delegate;
    private Map<Object, Object> keyMap;
    private Object eldestKey;

    public LruCache(Cache delegate) {
        this.delegate = delegate;
        setSize(1024);
    }

    @Override
    public void putObject(Object key, Object value) {
        delegate.putObject(key, value);
        cycleKeyList(key);
    }

    @Override
    public Object getObject(Object key) {
        keyMap.get(key); // 触发LRU
        return delegate.getObject(key);
    }

    private void cycleKeyList(Object key) {
        keyMap.put(key, key);
        if (eldestKey != null) {
            delegate.removeObject(eldestKey);
            eldestKey = null;
        }
    }
}

// 3. FifoCache - FIFO淘汰策略装饰器
public class FifoCache implements Cache {
    private final Cache delegate;
    private final Deque<Object> keyList;
    private int size;

    public FifoCache(Cache delegate) {
        this.delegate = delegate;
        this.keyList = new LinkedList<>();
        this.size = 1024;
    }

    @Override
    public void putObject(Object key, Object value) {
        cycleKeyList(key);
        delegate.putObject(key, value);
    }

    private void cycleKeyList(Object key) {
        keyList.addLast(key);
        if (keyList.size() > size) {
            Object oldestKey = keyList.removeFirst();
            delegate.removeObject(oldestKey);
        }
    }
}

// 4. SoftCache/WeakCache - 软引用/弱引用装饰器
public class SoftCache implements Cache {
    private final Cache delegate;
    private final Map<Object, Object> hardLinksToAvoidGarbageCollection;

    @Override
    public void putObject(Object key, Object value) {
        delegate.putObject(key, new SoftEntry(value));
    }

    @Override
    public Object getObject(Object key) {
        Object value = delegate.getObject(key);
        if (value instanceof SoftEntry) {
            return ((SoftEntry) value).get();
        }
        return value;
    }
}

// 5. BlockingCache - 阻塞装饰器(防止缓存击穿)
public class BlockingCache implements Cache {
    private final Cache delegate;
    private final ConcurrentHashMap<Object, CountDownLatch> locks;

    @Override
    public void putObject(Object key, Object value) {
        try {
            delegate.putObject(key, value);
        } finally {
            releaseLock(key);
        }
    }

    @Override
    public Object getObject(Object key) {
        acquireLock(key);
        Object value = delegate.getObject(key);
        if (value != null) {
            releaseLock(key);
        }
        return value;
    }

    private void acquireLock(Object key) {
        CountDownLatch latch = new CountDownLatch(1);
        CountDownLatch existing = locks.putIfAbsent(key, latch);
        if (existing != null) {
            try {
                existing.await();
            } catch (InterruptedException e) {
                Thread.interrupted();
            }
        }
    }
}

// 6. LoggingCache - 日志装饰器
public class LoggingCache implements Cache {
    private final Cache delegate;
    private int hits = 0;
    private int requests = 0;

    @Override
    public void putObject(Object key, Object value) {
        delegate.putObject(key, value);
    }

    @Override
    public Object getObject(Object key) {
        requests++;
        final Object value = delegate.getObject(key);
        if (value != null) {
            hits++;
        }
        if (log.isDebugEnabled()) {
            log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());
        }
        return value;
    }

    private double getHitRatio() {
        return (double) hits / (double) requests;
    }
}

// 7. ScheduledCache - 定时清理装饰器
public class ScheduledCache implements Cache {
    private final Cache delegate;
    private long clearInterval;
    private long lastClear;

    @Override
    public void putObject(Object key, Object value) {
        clearWhenStale();
        delegate.putObject(key, value);
    }

    @Override
    public Object getObject(Object key) {
        return clearWhenStale() ? null : delegate.getObject(key);
    }

    private boolean clearWhenStale() {
        if (System.currentTimeMillis() - lastClear > clearInterval) {
            clear();
            return true;
        }
        return false;
    }
}

// 8. SerializedCache - 序列化装饰器
public class SerializedCache implements Cache {
    private final Cache delegate;

    @Override
    public void putObject(Object key, Object value) {
        if (value != null && !(value instanceof Serializable)) {
            throw new CacheException("Cacheed object is not serializable: " + value);
        }
        delegate.putObject(key, serialize(value));
    }

    @Override
    public Object getObject(Object key) {
        Object object = delegate.getObject(key);
        return object == null ? null : deserialize((byte[]) object);
    }
}

// 9. SynchronizedCache - 同步装饰器
public class SynchronizedCache implements Cache {
    private final Cache delegate;

    @Override
    public synchronized void putObject(Object key, Object value) {
        delegate.putObject(key, value);
    }

    @Override
    public synchronized Object getObject(Object key) {
        return delegate.getObject(key);
    }

    @Override
    public synchronized void clear() {
        delegate.clear();
    }
}

// 10. TransactionalCache - 事务缓存装饰器
public class TransactionalCache implements Cache {
    private final Cache delegate;
    private boolean clearOnCommit;
    private Map<Object, Object> entriesToAddOnCommit;

    @Override
    public void putObject(Object key, Object value) {
        entriesToAddOnCommit.put(key, value);
    }

    @Override
    public Object getObject(Object key) {
        if (clearOnCommit) {
            return null;
        }
        return delegate.getObject(key);
    }

    public void commit() {
        if (clearOnCommit) {
            delegate.clear();
        }
        for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
            delegate.putObject(entry.getKey(), entry.getValue());
        }
        reset();
    }
}
2.2.3 Cache装饰器链的构建
java 复制代码
public class CacheBuilder {
    private String id;
    private Class<? extends Cache> implementation;
    private List<Class<? extends Cache>> decorators;

    public Cache build() {
        setDefaultImplementations();
        Cache cache = newBaseCacheInstance(implementation, id);
        setCacheProperties(cache);

        // 应用装饰器
        for (Class<? extends Cache> decorator : decorators) {
            cache = newCacheDecoratorInstance(decorator, cache);
            setCacheProperties(cache);
        }
        return cache;
    }
}

2.3 装饰器模式的应用场景

在MyBatis中,装饰器模式主要应用于:

  1. Executor增强:CachingExecutor为Executor添加二级缓存功能
  2. Cache功能扩展:通过各种Cache装饰器添加LRU、FIFO、日志等功能
  3. StatementHandler增强:通过RoutingStatementHandler路由到不同的实现

2.4 装饰器模式 vs 继承

装饰器模式相比继承的优势:

对比项 装饰器模式 继承
扩展方式 动态组合 静态继承
灵活性
代码复用 一般
类数量 可能增加 可能爆炸
复杂度 中等 简单

三、模板方法模式(Template Method Pattern)

模板方法模式是一种行为型设计模式,它在父类中定义了一个算法的框架,允许子类在不改变算法结构的情况下重写算法的特定步骤。

3.1 BaseExecutor

BaseExecutor是MyBatis中模板方法模式的典型应用,它定义了SQL执行的基本流程,将具体的执行逻辑留给子类实现。

3.1.1 BaseExecutor核心代码
java 复制代码
public abstract class BaseExecutor implements Executor {

    protected Transaction transaction;
    protected Executor wrapper;

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 1. 获取绑定SQL
        BoundSql boundSql = ms.getBoundSql(parameter);
        // 2. 创建缓存Key
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
        // 3. 调用模板方法
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }

    // 模板方法 - 定义算法骨架
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());

        if (closed) {
            throw new ExecutorException("Executor was closed.");
        }
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }

        List<E> list;
        try {
            queryStack++;
            // 4. 检查一级缓存
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            if (list != null) {
                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                // 5. 缓存未命中,调用抽象方法查询数据库
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            queryStack--;
        }

        if (queryStack == 0) {
            deferLoadIfNeeded();
        }
        return list;
    }

    // 从数据库查询 - 也是模板方法
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
            // 6. 调用抽象方法,由子类实现具体查询逻辑
            list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            localCache.removeObject(key);
        }
        localCache.putObject(key, list);
        return list;
    }

    // 抽象方法 - 由子类实现具体的查询逻辑
    protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter,
                                           RowBounds rowBounds, ResultHandler resultHandler,
                                           BoundSql boundSql) throws SQLException;

    // 抽象方法 - 由子类实现具体的更新逻辑
    protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

    // 其他抽象方法...
}
3.1.2 BaseExecutor的子类实现
java 复制代码
// 1. SimpleExecutor - 简单执行器
public class SimpleExecutor extends BaseExecutor {

    @Override
    protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            // 1. 创建Statement
            stmt = createStatement(ms, parameter, rowBounds);
            // 2. 设置参数
            setStatementParameters(ms, stmt, parameter, rowBounds, boundSql);
            // 3. 执行查询
            return handler.query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }

    @Override
    protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
            stmt = prepareStatement(handler, ms.getStatementLog());
            return handler.update(stmt);
        } finally {
            closeStatement(stmt);
        }
    }
}

// 2. ReuseExecutor - 重用执行器
public class ReuseExecutor extends BaseExecutor {
    private final Map<String, Statement> statementMap = new HashMap<>();

    @Override
    protected <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
        Statement stmt = prepareStatement(handler, ms.getStatementLog(), true);
        return handler.query(stmt, resultHandler);
    }

    private Statement prepareStatement(StatementHandler handler, Log statementLog, boolean isReusable) throws SQLException {
        BoundSql boundSql = handler.getBoundSql();
        String sql = boundSql.getSql();
        Statement stmt;
        if (isReusable) {
            // 尝试重用Statement
            stmt = statementMap.get(sql);
            if (stmt == null) {
                stmt = handler.prepare(statementLog.getConnection(), transaction.getTimeout());
                statementMap.put(sql, stmt);
            }
        } else {
            stmt = handler.prepare(statementLog.getConnection(), transaction.getTimeout());
        }
        handler.parameterize(stmt);
        return stmt;
    }
}

// 3. BatchExecutor - 批量执行器
public class BatchExecutor extends BaseExecutor {
    private final List<Statement> statementList = new ArrayList<>();
    private final List<BatchResult> batchResultList = new ArrayList<>();

    @Override
    protected int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Configuration configuration = ms.getConfiguration();
        StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
        Statement stmt = prepareStatement(handler, ms.getStatementLog());
        handler.parameterize(stmt);
        batchResultList.add(new BatchResult(ms.getStatement(), ms.getId(), parameter));
        handler.batch(stmt);
        return BATCH_UPDATE_RETURN_VALUE;
    }

    @Override
    public List<BatchResult> doFlushStatements(boolean isRollback) {
        List<BatchResult> results = new ArrayList<>();
        try {
            for (int i = 0, n = statementList.size(); i < n; i++) {
                Statement stmt = statementList.get(i);
                BatchResult batchResult = batchResultList.get(i);
                try {
                    int[] updateCounts = stmt.executeBatch();
                    batchResult.setUpdateCounts(updateCounts);
                    results.add(batchResult);
                } catch (BatchUpdateException e) {
                    StringBuilder message = new StringBuilder();
                    message.append(batchResult.getStatement()).append(" failed.");
                    if (updateCounts != null && updateCounts.length > 0) {
                        message.append(" Update counts: ");
                        for (int j = 0; j < updateCounts.length; j++) {
                            message.append(updateCounts[j]).append(";");
                        }
                    }
                    throw new BatchExecutorException(message.toString(), e, updateCounts, batchResult.getStatement());
                }
            }
            return results;
        } finally {
            clearLocalCache();
            statementList.clear();
            batchResultList.clear();
        }
    }
}

3.2 BaseTypeHandler

BaseTypeHandler也是模板方法模式的应用,它定义了类型处理的基本流程。

3.2.1 BaseTypeHandler核心代码
java 复制代码
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        // 模板方法:设置非空参数
        setParameter(ps, i, parameter, jdbcType);
    }

    @Override
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
        // 模板方法:通过列名获取结果
        T result = getNullableResult(rs, columnName);
        return rs.wasNull() ? null : result;
    }

    @Override
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        // 模板方法:通过列索引获取结果
        T result = getNullableResult(rs, columnIndex);
        return rs.wasNull() ? null : result;
    }

    @Override
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        // 模板方法:从存储过程获取结果
        T result = getNullableResult(cs, columnIndex);
        return cs.wasNull() ? null : result;
    }

    // 抽象方法:由子类实现具体的参数设置逻辑
    public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

    // 抽象方法:由子类实现具体的结果获取逻辑
    public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

    public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;

    public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}
3.2.2 具体的TypeHandler实现
java 复制代码
// StringTypeHandler
public class StringTypeHandler extends BaseTypeHandler<String> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, parameter);
    }

    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

// IntegerTypeHandler
public class IntegerTypeHandler extends BaseTypeHandler<Integer> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter);
    }

    @Override
    public Integer getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getInt(columnName);
    }

    @Override
    public Integer getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getInt(columnIndex);
    }

    @Override
    public Integer getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getInt(columnIndex);
    }
}

3.3 模板方法模式的优势

  1. 代码复用:将公共逻辑提取到父类,避免重复代码
  2. 扩展性强:子类可以灵活实现特定步骤
  3. 算法稳定:算法框架固定,保证一致性
  4. 易于维护:修改算法只需修改父类

四、策略模式(Strategy Pattern)

策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以相互替换。策略模式让算法独立于使用它的客户端。

4.1 RoutingStatementHandler

RoutingStatementHandler是MyBatis中策略模式的典型应用,它根据MappedStatement的配置,路由到不同的StatementHandler实现。

4.1.1 RoutingStatementHandler源码
java 复制代码
public class RoutingStatementHandler implements StatementHandler {

    private final StatementHandler delegate;

    public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        // 根据Statement类型选择策略
        switch (ms.getStatementType()) {
            case STATEMENT:
                delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case PREPARED:
                delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            case CALLABLE:
                delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                break;
            default:
                throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
        }
    }

    @Override
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
        return delegate.prepare(connection, transactionTimeout);
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        delegate.parameterize(statement);
    }

    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        return delegate.query(statement, resultHandler);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        return delegate.update(statement);
    }

    // 其他方法都委托给delegate...
}
4.1.2 三种StatementHandler策略
java 复制代码
// 1. SimpleStatementHandler - 简单SQL策略
public class SimpleStatementHandler extends BaseStatementHandler {

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        if (mappedStatement.getResultSetType() == ResultSetType.FORWARD_ONLY) {
            return connection.createStatement();
        } else {
            return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
    }

    @Override
    public int update(Statement statement) throws SQLException {
        String sql = boundSql.getSql();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        int rows;
        if (keyGenerator instanceof Jdbc3KeyGenerator) {
            statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
            rows = statement.getUpdateCount();
            keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
        } else if (keyGenerator instanceof SelectKeyGenerator) {
            statement.execute(sql);
            rows = statement.getUpdateCount();
            keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
        } else {
            statement.execute(sql);
            rows = statement.getUpdateCount();
        }
        return rows;
    }
}

// 2. PreparedStatementHandler - 预编译SQL策略
public class PreparedStatementHandler extends BaseStatementHandler {

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getResultSetType() == ResultSetType.FORWARD_ONLY) {
            return connection.prepareStatement(sql);
        } else {
            return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        parameterHandler.setParameters((PreparedStatement) statement);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        int rows = ps.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
        return rows;
    }
}

// 3. CallableStatementHandler - 存储过程策略
public class CallableStatementHandler extends BaseStatementHandler {

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        if (mappedStatement.getResultSetType() == ResultSetType.FORWARD_ONLY) {
            return connection.prepareCall(sql);
        } else {
            return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        }
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        registerOutputParameters((CallableStatement) statement);
        parameterHandler.setParameters((CallableStatement) statement);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        CallableStatement cs = (CallableStatement) statement;
        cs.execute();
        int rows = cs.getUpdateCount();
        Object parameterObject = boundSql.getParameterObject();
        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        keyGenerator.processAfter(executor, mappedStatement, cs, parameterObject);
        return rows;
    }
}

public class Configuration {

    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null ? defaultExecutorType : executorType;
        Executor executor;
        // 根据ExecutorType选择策略
        if (ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if (ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            executor = new SimpleExecutor(this, transaction);
        }
        // 如果启用缓存,装饰为CachingExecutor
        if (cacheEnabled) {
            executor = new CachingExecutor(executor);
        }
        return (Executor) interceptorChain.pluginAll(executor);
    }
}
4.2.2 三种Executor策略对比
Executor类型 特点 适用场景 性能
SimpleExecutor 每次执行创建新Statement 一般查询、单条操作 中等
ReuseExecutor 重用Statement 执行相同SQL多次 较好
BatchExecutor 批量执行SQL 批量插入、更新 最好

4.3 策略模式的优势

  1. 算法可替换:可以灵活切换不同的算法实现
  2. 代码解耦:算法的实现与使用分离
  3. 扩展方便:新增策略只需实现新类
  4. 简化测试:可以独立测试每个策略

4.4 策略模式 vs 模板方法模式

对比项 策略模式 模板方法模式
算法结构 可变 固定
实现方式 组合 继承
灵活性 中等
复杂度 较高 较低

五、设计模式的综合应用

在MyBatis中,多种设计模式往往协同工作,共同完成复杂的功能。

5.1 Executor的创建过程

java 复制代码
// 1. 模板方法模式:BaseExecutor定义执行流程
// 2. 策略模式:根据ExecutorType选择SimpleExecutor、ReuseExecutor或BatchExecutor
// 3. 装饰器模式:CachingExecutor为Executor添加缓存功能
// 4. 代理模式:Plugin为Executor添加拦截功能

Executor executor = configuration.newExecutor(transaction, ExecutorType.SIMPLE);

5.2 Cache的构建过程

java 复制代码
// 1. 装饰器模式:多层Cache装饰器嵌套
// 2. 模板方法模式:BaseExecutor定义查询流程(使用缓存)

Cache cache = new CacheBuilder("myCache")
    .implementation(PerpetualCache.class)
    .addDecorator(LruCache.class)
    .addDecorator(FifoCache.class)
    .addDecorator(ScheduledCache.class)
    .build();

5.3 StatementHandler的选择过程

java 复制代码
// 1. 策略模式:RoutingStatementHandler路由到不同的StatementHandler
// 2. 模板方法模式:BaseStatementHandler定义Statement处理流程
// 3. 代理模式:LoggerStatementHandler添加日志功能

StatementHandler handler = configuration.newStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);

六、最佳实践与建议

6.1 装饰器模式应用场景

当满足以下条件时,可以考虑使用装饰器模式:

  1. 需要动态地、透明地给对象添加功能
  2. 不希望通过继承来扩展功能(避免类爆炸)
  3. 需要组合多个功能

应用示例:

java 复制代码
// 为服务添加缓存、日志、监控功能
Service service = new LoggingService(
    new MonitoringService(
        new CachingService(
            new BasicService()
        )
    )
);

6.2 模板方法模式应用场景

当满足以下条件时,可以考虑使用模板方法模式:

  1. 算法的整体结构固定,部分步骤可变
  2. 多个子类有共同的行为逻辑
  3. 需要控制子类的扩展

应用示例:

java 复制代码
public abstract class AbstractDataService {

    // 模板方法:定义数据处理流程
    public final void processData(String data) {
        validate(data);      // 验证
        data = transform(data); // 转换
        save(data);          // 保存
    }

    protected abstract void validate(String data);
    protected abstract String transform(String data);
    protected abstract void save(String data);
}

6.3 策略模式应用场景

当满足以下条件时,可以考虑使用策略模式:

  1. 有多种算法可以相互替换
  2. 算法的使用和实现分离
  3. 需要在运行时选择算法

应用示例:

java 复制代码
// 支付策略
public interface PaymentStrategy {
    void pay(int amount);
}

public class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        // 信用卡支付逻辑
    }
}

public class WeChatPayPayment implements PaymentStrategy {
    public void pay(int amount) {
        // 微信支付逻辑
    }
}

// 上下文
public class PaymentContext {
    private PaymentStrategy strategy;

    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void processPayment(int amount) {
        strategy.pay(amount);
    }
}

6.4 设计模式选择指南

场景 推荐模式 理由
需要动态添加功能 装饰器模式 灵活组合,避免继承
算法结构固定部分可变 模板方法模式 代码复用,结构清晰
算法需要相互替换 策略模式 解耦算法和使用
创建复杂对象 构建者模式 分步构建,代码清晰
创建对象族 工厂模式 解耦创建和使用
控制对象访问 代理模式 增强功能,不修改原类

6.5 避免过度设计

虽然设计模式很有用,但也要避免过度设计:

  1. 简单问题简单解决:不要为了使用模式而使用模式
  2. 按需设计:根据实际需求选择合适的模式
  3. 保持简洁:简单的设计往往比复杂的设计更好
  4. 重构时再引入:先实现功能,重构时再应用模式

七、总结

本文深入讲解了MyBatis中的装饰器模式、模板方法模式和策略模式,通过源码分析和示例代码,帮助读者理解这些设计模式在实际框架中的应用。

7.1 关键要点

装饰器模式:

  • CachingExecutor为Executor添加二级缓存功能
  • Cache装饰器链提供LRU、FIFO、日志等多种功能
  • 动态组合,灵活扩展

模板方法模式:

  • BaseExecutor定义SQL执行的基本流程
  • BaseTypeHandler定义类型处理的基本流程
  • 代码复用,算法稳定

策略模式:

  • RoutingStatementHandler根据配置路由到不同的StatementHandler
  • 不同Executor策略适用于不同的执行场景
  • 算法可替换,代码解耦

7.2 设计模式的价值

  1. 提高代码质量:使代码更易读、易维护、易扩展
  2. 促进架构设计:提供成熟的设计思想和解决方案
  3. 增强团队协作:统一的设计语言和思维方式
  4. 提升个人能力:深入理解设计模式是成为高级工程师的必经之路

相关推荐
刘一说9 小时前
2026年Java技术栈全景图:从Web容器到云原生的深度选型指南(附避坑指南)
java·前端·spring boot·后端·云原生·tomcat·mybatis
独自归家的兔13 小时前
Spring Retryable 注解完全指南:从入门到精通,让接口容错更简单
java·spring·mybatis
sunddy_x13 小时前
MyBatis 与 SpringBoot 集成入门
spring boot·后端·mybatis
雨中飘荡的记忆14 小时前
MyBatis插件模块详解
mybatis
hashiqimiya14 小时前
mybatis搭建项目框架
java·spring·mybatis
计算机学姐1 天前
基于SpringBoot的美妆销售系统【个性化推荐算法+数据可视化统计+库存预警+物流信息】
java·vue.js·spring boot·后端·mysql·信息可视化·mybatis
while(1){yan}1 天前
Mybatis动态SQL
数据库·spring boot·sql·spring·java-ee·mybatis
hssfscv2 天前
Javaweb学习笔记——JDBC和Mybatis
笔记·学习·mybatis