1. MyBatis框架与设计模式概述
MyBatis作为Java生态中最受欢迎的ORM框架之一,其内部实现巧妙地运用了多种经典设计模式。这些模式不是随意堆砌,而是为了解决特定场景下的架构问题而精心设计的。理解这些模式的应用场景和实现方式,对于深入掌握MyBatis工作原理、编写高质量持久层代码具有重要意义。
在实际开发中,我们经常遇到这样的场景:当需要扩展MyBatis功能时,如果对其内部设计模式缺乏了解,很容易写出违背框架设计初衷的代码。比如错误地覆盖核心组件、重复造轮子,或者无法充分利用框架提供的扩展点。通过源码层面的模式解析,开发者可以真正做到"知其然更知其所以然"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 创建型模式在MyBatis中的应用
2.1 工厂方法模式(Factory Method)
在SqlSessionFactory构建过程中,MyBatis典型地运用了工厂方法模式。SqlSessionFactory作为顶级接口,定义了创建SqlSession的工厂方法,而具体的实现逻辑则由DefaultSqlSessionFactory和SqlSessionManager这两个实现类完成。
java复制public interface SqlSessionFactory {
SqlSession openSession();
// 其他重载方法...
}
这种设计带来的好处是:
- 将对象创建的逻辑封装起来,客户端只需要关心接口而不用关心具体实现
- 便于扩展新的Session创建方式,比如可以轻松添加对分布式Session的支持
- 创建过程可以加入缓存、代理等增强逻辑
实际开发中,我们通常通过XML配置文件或Java Config方式初始化SqlSessionFactory,这正是工厂方法模式的典型应用场景。
2.2 建造者模式(Builder)
MyBatis的配置解析过程大量使用了建造者模式。以XMLConfigBuilder为例,它专门负责解析mybatis-config.xml配置文件:
java复制public class XMLConfigBuilder extends BaseBuilder {
public Configuration parse() {
// 分步骤解析各种配置项
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
// 依次解析各个配置节点
propertiesElement(root.evalNode("properties"));
typeAliasesElement(root.evalNode("typeAliases"));
// ...其他配置解析
}
}
建造者模式在这里的优势体现在:
- 将复杂配置对象的构建过程分解为多个步骤
- 允许按不同顺序和组合方式构建配置
- 最终生成不可变的Configuration对象,保证线程安全
3. 结构型模式解析
3.1 代理模式(Proxy)
MyBatis最核心的代理应用就是在Mapper接口的实现上。当我们定义一个Mapper接口:
java复制public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User selectUser(int id);
}
MyBatis会通过JDK动态代理生成实现类,核心逻辑在MapperProxy类中:
java复制public class MapperProxy<T> implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
// 将方法调用转换为SQL执行
return mapperMethod.execute(sqlSession, args);
}
}
代理模式在此处的精妙之处在于:
- 接口与实现完全解耦,不需要手动编写实现类
- 可以在代理层统一加入缓存、日志等横切逻辑
- 支持方法级别的SQL定制,保持代码的简洁性
3.2 装饰器模式(Decorator)
MyBatis的缓存模块大量使用了装饰器模式。以PerpetualCache为基础缓存实现,其他缓存实现如LruCache、FifoCache等都是它的装饰器:
java复制public class LruCache implements Cache {
private final Cache delegate;
public LruCache(Cache delegate) {
this.delegate = delegate;
// 初始化LRU相关逻辑
}
public void putObject(Object key, Object value) {
delegate.putObject(key, value);
// 添加LRU特定逻辑
}
// 其他方法...
}
这种设计带来的好处是:
- 可以动态地给缓存添加各种功能
- 各个功能模块相互独立,可以自由组合
- 符合开闭原则,新增功能无需修改现有代码
4. 行为型模式深度剖析
4.1 模板方法模式(Template Method)
BaseExecutor类完美展示了模板方法模式的应用。它定义了执行SQL的骨架,而将具体步骤延迟到子类实现:
java复制public abstract class BaseExecutor implements Executor {
public <E> List<E> query(MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler) {
// 1. 获取绑定SQL
BoundSql boundSql = ms.getBoundSql(parameter);
// 2. 创建缓存key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 3. 实际查询方法(由子类实现)
return queryFromDatabase(ms, parameter, rowBounds, resultHandler, boundSql, key);
}
protected abstract <E> List<E> queryFromDatabase(...);
}
模板方法模式在此处的优势:
- 固定了SQL执行的主要流程
- 允许不同Executor实现各自特有的查询逻辑
- 便于统一添加缓存、事务等公共逻辑
4.2 策略模式(Strategy)
MyBatis的日志模块采用了典型的策略模式。Log接口定义了日志操作的抽象,而具体的日志实现(如Log4j2Impl、Slf4jImpl等)作为具体策略:
java复制public interface Log {
boolean isDebugEnabled();
void debug(String s);
// 其他日志级别方法...
}
public class Log4j2Impl implements Log {
private final Logger log;
public Log4j2Impl(String clazz) {
log = LogManager.getLogger(clazz);
}
public void debug(String s) {
log.debug(s);
}
}
策略模式的优点在此体现为:
- 可以运行时动态切换日志实现
- 新增日志框架支持不影响现有代码
- 各种日志实现可以独立测试和演进
5. 复合模式与特殊场景应用
5.1 责任链模式(Interceptor Chain)
MyBatis的插件机制基于责任链模式实现。Interceptor接口定义拦截点,多个Interceptor组成调用链:
java复制public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
// ...
}
// 实际调用过程
public class Plugin implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) {
// 依次调用拦截器链
if (interceptors.size() == 0) {
return method.invoke(target, args);
}
return new Invocation(target, method, args, interceptors).proceed();
}
}
这种设计的精妙之处在于:
- 可以无侵入地增强MyBatis核心功能
- 拦截器之间相互独立,可以灵活组合
- 支持方法级别的精确拦截控制
5.2 组合模式(SQL节点处理)
MyBatis处理动态SQL时使用了组合模式。SqlNode接口表示SQL节点,各种实现类如IfSqlNode、ForEachSqlNode等构成树形结构:
java复制public interface SqlNode {
boolean apply(DynamicContext context);
}
public class IfSqlNode implements SqlNode {
private final String test;
private final SqlNode contents;
public boolean apply(DynamicContext context) {
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}
}
组合模式在此场景的优势:
- 可以递归处理复杂的SQL节点结构
- 新增节点类型不影响现有处理逻辑
- 统一了简单和复杂SQL节点的处理接口
6. 设计模式实战应用技巧
6.1 基于设计模式的MyBatis扩展
理解这些设计模式后,我们可以更好地扩展MyBatis。比如实现一个自定义插件:
java复制@Intercepts({
@Signature(type= Executor.class, method="query",
args={MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class QueryTimeInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
long start = System.currentTimeMillis();
Object result = invocation.proceed();
long end = System.currentTimeMillis();
System.out.println("Query took: " + (end - start) + "ms");
return result;
}
}
这个插件利用了责任链模式,可以无侵入地监控SQL执行时间。
6.2 常见问题排查指南
-
Mapper接口方法不生效
- 检查是否使用了正确的代理模式
- 确认方法签名与Mapper XML中的定义匹配
- 验证SqlSession是否正常创建
-
动态SQL解析异常
- 检查组合模式下的节点嵌套是否正确
- 验证OGNL表达式语法
- 确认各SqlNode实现类的使用场景
-
缓存失效问题
- 分析装饰器模式的包装顺序
- 检查CacheKey的生成逻辑
- 验证事务隔离级别的影响
7. 性能优化与最佳实践
7.1 基于设计模式的优化策略
- Executor选择策略
- SimpleExecutor:适合简单场景
- ReuseExecutor:重用预处理语句
- BatchExecutor:批量操作优化
java复制// 在配置中指定Executor类型
@Bean
public SqlSessionFactory sqlSessionFactory() {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setConfiguration(configuration());
return factory.getObject();
}
private Configuration configuration() {
Configuration config = new Configuration();
config.setDefaultExecutorType(ExecutorType.BATCH);
return config;
}
- 缓存装饰器选择
- LruCache:基于最近最少使用算法
- FifoCache:先进先出策略
- ScheduledCache:定时刷新缓存
7.2 设计模式应用的最佳实践
-
插件开发原则
- 明确拦截目标(Executor/StatementHandler等)
- 避免过度拦截影响性能
- 注意线程安全问题
-
动态SQL编写建议
- 合理组合各种SqlNode
- 避免过于复杂的嵌套结构
- 考虑使用
