作为Java开发者,你一定用过MyBatis,但你真的了解它背后的设计精髓吗?本文将带你深入MyBatis装饰器、模板方法、策略三大设计模式的实战应用,看完保证你的代码水平暴涨!
开篇:为什么要学习MyBatis的设计模式?
MyBatis作为国内最流行的持久层框架之一,其源码中设计模式的运用堪称教科书级别。今天我们就来拆解其中最核心的3种模式:
✅ 装饰器模式 - 像搭积木一样动态添加功能✅ 模板方法模式 - 定义骨架,细节交给子类✅ 策略模式 - 算法随意切换,解耦到极致第一式:装饰器模式 - 功能叠加的艺术
什么是装饰器模式?
想象你开了一家奶茶店,基础款是纯牛奶,客户可以加珍珠、加椰果、加布丁...每加一样就是一层"装饰"。装饰器模式就是这个道理——不改变原对象,动态添加新功能。
装饰器模式
MyBatis中的经典案例:CachingExecutor
MyBatis的二级缓存就是用装饰器模式实现的!看核心代码:
public class CachingExecutor implements Executor { private final Executor delegate; // 被装饰的真正执行器 private final TransactionalCacheManager tcm; public CachingExecutor(Executor delegate) { this.delegate = delegate; } @Override public <E> List<E> query(...) throws SQLException { Cache cache = ms.getCache(); if (cache != null) { // 先查缓存 List<E> list = tcm.getObject(cache, key); if (list == null) { // 缓存没有,委托给原执行器查询 list = delegate.query(...); // 放入缓存 tcm.putObject(cache, key, list); } return list; } // 没开缓存,直接用原执行器 return delegate.query(...); }}关键点:
- CachingExecutor包装了一个真正的Executor
- 先查缓存,没有才调用被包装的Executor
- 完全不修改原Executor的代码!
Cache装饰器链 - 乐高式组装
更牛的是MyBatis的缓存系统,多个装饰器可以层层嵌套:
Cache装饰器链
MyBatis提供了10种Cache装饰器:
装饰器 | 功能 | 使用场景 |
LruCache | LRU淘汰算法 | 内存有限时 |
FifoCache | FIFO淘汰算法 | 简单场景 |
SoftCache | 软引用 | 防止OOM |
WeakCache | 弱引用 | 临时缓存 |
BlockingCache | 防缓存击穿 | 高并发场景 |
LoggingCache | 日志统计 | 监控缓存命中率 |
ScheduledCache | 定时清理 | 定期刷新数据 |
SerializedCache | 序列化存储 | 分布式缓存 |
SynchronizedCache | 线程同步 | 保证线程安全 |
TransactionalCache | 事务支持 | 配合事务使用 |
核心代码示例:
// LruCache - 最近最少使用淘汰public class LruCache implements Cache { private final Cache delegate; private Map<Object, Object> keyMap; @Override public void putObject(Object key, Object value) { delegate.putObject(key, value); cycleKeyList(key); // 触发LRU淘汰 }}// BlockingCache - 防止缓存击穿public class BlockingCache implements Cache { private final Cache delegate; private final ConcurrentHashMap<Object, CountDownLatch> locks; @Override public Object getObject(Object key) { acquireLock(key); // 加锁 Object value = delegate.getObject(key); if (value != null) { releaseLock(key); // 有值则释放锁 } return value; }}装饰器模式的优势
✅ 不修改原类 - 通过包装扩展功能✅ 动态组合 - 运行时灵活添加功能✅ 职责单一 - 每个装饰器只做一件事✅ 无限嵌套 - 像搭积木一样组合功能第二式:模板方法模式 - 框架设计的基石
什么是模板方法模式?
就像做菜有固定步骤:备菜→炒制→调味→装盘,但每道菜的具体做法不同。模板方法模式就是定义算法骨架,具体步骤由子类实现。
模板方法模式
MyBatis中的baseExecutor
baseExecutor是模板方法模式的经典案例,它定义了SQL执行的标准流程:
public abstract class baseExecutor implements Executor { // 模板方法 - 定义查询的整体流程 @Override public <E> List<E> query(...) throws SQLException { // 1. 创建缓存Key CacheKey key = createCacheKey(...); List<E> list; queryStack++; // 2. 检查一级缓存 list = localCache.getObject(key); if (list != null) { return list; // 缓存命中 } // 3. 缓存未命中,查询数据库 list = queryFromDatabase(...); // 4. 放入缓存 localCache.putObject(key, list); return list; } // 抽象方法 - 由子类实现具体查询逻辑 protected abstract <E> List<E> doQuery(...); protected abstract int doUpdate(...);}三种Executor子类实现
Executor策略选择
1️⃣ SimpleExecutor - 最简单直接
public class SimpleExecutor extends baseExecutor { @Override protected <E> List<E> doQuery(...) { Statement stmt = null; try { // 每次都创建新Statement stmt = createStatement(...); // 执行查询 return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); // 用完就关闭 } }}2️⃣ ReuseExecutor - 重用Statement
public class ReuseExecutor extends baseExecutor { private final Map<String, Statement> statementMap = new HashMap<>(); @Override protected <E> List<E> doQuery(...) { String sql = boundSql.getSql(); // 尝试重用已有的Statement Statement stmt = statementMap.get(sql); if (stmt == null) { stmt = prepareStatement(...); statementMap.put(sql, stmt); // 缓存起来 } return handler.query(stmt, resultHandler); }}3️⃣ BatchExecutor - 批量执行
public class BatchExecutor extends baseExecutor { private final List<Statement> statementList = new ArrayList<>(); @Override protected int doUpdate(...) { // 不立即执行,先加入批处理列表 handler.batch(stmt); return BATCH_UPDATE_RETURN_VALUE; } @Override public List<BatchResult> doFlushStatements(...) { // 统一执行所有批处理 for (Statement stmt : statementList) { stmt.executeBatch(); } return results; }}三种Executor对比
Executor类型 | 特点 | 适用场景 | 性能 |
SimpleExecutor | 每次新建Statement | 普通查询 | ⭐⭐⭐ |
ReuseExecutor | 重用Statement | 相同SQL多次执行 | ⭐⭐⭐⭐ |
BatchExecutor | 批量执行 | 大量插入/更新 | ⭐⭐⭐⭐⭐ |
模板方法模式的威力
✅ 代码复用 - 公共逻辑在父类,避免重复✅ 扩展灵活 - 新增子类很简单✅ 流程统一 - 保证算法结构一致✅ 易于维护 - 修改流程只需改父类第三式:策略模式 - 算法自由切换
什么是策略模式?
就像出行方式:走路、骑车、开车、坐地铁,目的地相同但策略不同。策略模式就是定义一系列算法,让它们可以互相替换。
RoutingStatementHandler
MyBatis中的RoutingStatementHandler
RoutingStatementHandler根据SQL类型自动选择合适的处理器:
public class RoutingStatementHandler implements StatementHandler { private final StatementHandler delegate; public RoutingStatementHandler(...) { // 根据SQL类型选择策略 switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(...); break; case PREPARED: delegate = new PreparedStatementHandler(...); break; case CALLABLE: delegate = new CallableStatementHandler(...); break; } } @Override public int update(Statement statement) { // 委托给具体策略执行 return delegate.update(statement); }}三种StatementHandler策略
核心区别代码示例:
// SimpleStatementHandler - 直接执行SQLString sql = "SELECT * FROM user WHERe id = 1";statement.execute(sql);// PreparedStatementHandler - 预编译SQL(推荐)String sql = "SELECt * FROM user WHERe id = ?";PreparedStatement ps = connection.prepareStatement(sql);ps.setInt(1, userId);ps.execute();// CallableStatementHandler - 调用存储过程String sql = "{call getUserById(?)}";CallableStatement cs = connection.prepareCall(sql);cs.setInt(1, userId);cs.execute();Executor的策略选择
MyBatis启动时可以配置使用哪种Executor:
public class Configuration { public Executor newExecutor(Transaction tx, ExecutorType type) { Executor executor; // 策略选择 if (ExecutorType.BATCH == type) { executor = new BatchExecutor(this, tx); } else if (ExecutorType.REUSE == type) { executor = new ReuseExecutor(this, tx); } else { executor = new SimpleExecutor(this, tx); } // 如果开启缓存,再装饰一层 if (cacheEnabled) { executor = new CachingExecutor(executor); } return executor; }}策略模式的强大之处
✅ 算法解耦 - 使用和实现分离✅ 易于切换 - 运行时动态选择✅ 易于扩展 - 新增策略很简单✅ 消除if-else - 代码更优雅三大模式的协同作战
MyBatis的强大之处在于多种模式的完美配合:
设计模式综合应用
// 一个完整的SQL执行流程// 1. 策略模式 - 选择Executor类型Executor executor = configuration.newExecutor( transaction, ExecutorType.SIMPLE // 选择策略);// 2. 装饰器模式 - 添加缓存功能if (cacheEnabled) { executor = new CachingExecutor(executor); // 装饰}// 3. 模板方法模式 - 执行查询// baseExecutor定义流程:// 创建CacheKey → 查一级缓存 → 调用doQuery()List<User> users = executor.query(...);装饰器模式适用场景
适合:
1.需要动态添加功能(日志、缓存、权限)2.不想通过继承扩展(避免类爆炸)3.需要组合多个功能不适合:
1.功能简单固定2.性能要求极高(多层嵌套有开销)模板方法模式适用场景
适合:
1.算法结构固定,部分步骤可变2.多个类有相同的处理流程3.需要控制子类扩展点不适合:
1.流程经常变化2.子类差异太大策略模式适用场景
适合:
1.多种算法可互相替换2.需要运行时选择算法3.消除大量if-else不适合:
1.策略很少变化2.客户端需要了解所有策略模式选择速查表
需求 | 推荐模式 | 原因 |
动态添加功能 | 装饰器 | 灵活组合,不改原类 |
算法流程固定 | 模板方法 | 代码复用,扩展方便 |
算法需要切换 | 策略 | 解耦使用和实现 |
创建复杂对象 | 建造者 | 分步构建 |
对象创建分类 | 工厂 | 统一创建入口 |
总结:从MyBatis学到的设计智慧
装饰器模式:
1.CachingExecutor为Executor加缓存2.Cache装饰器链实现LRU、FIFO等功能3.核心思想:包装而非继承模板方法模式:
1.baseExecutor定义SQL执行流程2.SimpleExecutor、ReuseExecutor、BatchExecutor实现细节3.核心思想:定义骨架,细节可变策略模式:
1.RoutingStatementHandler路由到不同Handler2.Executor类型可配置切换3.核心思想:算法解耦,自由替换设计模式的价值
Mybatis整体架构
写在最后
MyBatis作为国内最流行的持久层框架,其源码中的设计模式应用堪称教科书。学设计模式不是为了炫技,而是为了写出更优雅、更易维护的代码。
