从Mybatis源码学会了什么

85 阅读6分钟

摘要:MyBatis源码展现了优秀的设计模式和架构思想。其运用建造者模式创建复杂对象,动态代理实现Mapper接口,装饰器动态扩展功能等。架构上采用清晰分层设计,插件化扩展机制实现开闭原则,多级缓存提升性能,面向接口编程降低耦合。这些设计理念对日常业务开发也有很大借鉴价值。

通过以下系列博文,我们熟悉了Mybatis源码实现的诸多细节,如Executor接口及其实现、缓存体系、自定义插件等。

  1. Mybatis入门到精通 一
  2. Mybatis的Executor和缓存体系
  3. Mybatis二级缓存实现详解
  4. Mybatis执行Mapper过程详解
  5. Mybatis插件原理及分页插件
  6. Spring集成Mybatis原理详解

这次,让我们跳出局部,看看Mybatis在设计和架构上,有哪些值得我们学习的地方。

注:本文中源码来自mybatis 3.4.x版本,地址github.com/mybatis/myb…

一 设计模式

1.1 建造者模式

MyBatis 大量使用建造者模式解决「复杂对象初始化」问题,比如 SqlSessionFactoryBuilderXMLConfigBuilderMapperBuilderAssistant 等。

SqlSessionFactoryBuilder为例,在创建SqlSessionFactory前需要先解析主配置文件,这个过程非常繁琐。使用建造者模式:

  • 分离「对象构建逻辑」和「对象使用逻辑」,让复杂对象创建流程清晰;
  • 建造者可以分步骤校验配置正确性,避免无效对象产生;
  • 多重载的 build 方法, 适配不同入参场景 。
public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  // 简化代码:reader就是配置文件的读取流
  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    }
  }

1.2 工厂模式

工厂模式隐藏对象创建细节,上层无需关心「具体实现类」,只依赖接口。例如

  • SqlSessionFactory 负责创建 SqlSession,提供了多个重载实现。
  • Executor、StatementHandler、ResultSetHandler、ParameterHandler只能由 Configuration 中的 newExecutor 等方法创建,根据配置选择不同实现类;同时应用自定义插件。

1.3 动态代理

MyBatis 核心的特性之一是「Mapper 接口无需实现类」,底层通过 JDK 动态代理,实现接口方法调用触发SQL执行。详见 MapperProxyFactory,缓存方法元数据,避免重复解析。

// 缓存mapper方法元数据,避免重复解析
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();

此外,插件原理也是动态代理,定义Interceptor接口声明代理逻辑、代理对象创建等。使用户可以自定义插件实现。

1.4 模板方法

  • 模板方法将「不变的通用逻辑」抽离到父类,「可变的差异化逻辑」交给子类实现;
  • 避免子类重复编写通用代码(如缓存检查、参数校验),降低代码冗余;
  • 父类控制流程,子类只关注核心逻辑,符合「开闭原则」。

BaseExecutor 实现了模板方法模式,将 Executor 的通用流程(如参数处理、SQL 执行、结果集处理)抽象为模板方法,具体子类(SimpleExecutor/BatchExecutor/ReuseExecutor)只需实现差异化逻辑。

// 模板方法,一级缓存使用逻辑
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
// ...,会调用doQuer()
    
}

// 由子类实现
protected abstract <E> List<E> doQuery(...)

1.5 装饰模式

用装饰模式替代继承,无需定义子类,就能给对象动态增加职责。

如通过CachingExecutor装饰,给BaseExecutor增加二级缓存能力。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // 对BaseExecutor子类进行装饰
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }
    // 省略...
}

如Cache接口体系,定义了很多装饰器,根据用户配置,实现功能特性自由组合。

1.6 策略模式

Executor接口可根据用户配置,运行时可切换SimpleExecutor、ReuseExecutor或BatchExecutor,实现不同的SQL执行策略。

RoutingStatementHandler 根据配置路由到不同的 StatementHandler实现。

1.7 注册中心

源码中大量使用 Registry 模式来管理可扩展组件, 可以统一初始化、统一查找、统一生命周期;例如:

  • TypeHandlerRegistry
  • MapperRegistry
  • LanguageDriverRegistry
  • CacheRegistry

二 架构思维

2.1 分层设计

MyBatis的核心分层非常清晰,每层只做一件事,符合「单一职责原则」: image.png

  • 各层职责
    • Mapper 层:用户接口,定义 SQL 操作;
    • SqlSession 层:会话入口,封装 Executor 调用;
    • Executor 层:执行器,处理缓存、事务、SQL 执行流程;
    • StatementHandler 层:处理 SQL 拼接、Statement 创建;
    • Parameter/ResultSetHandler 层:参数绑定、结果集映射;
  • 架构思维
    • 分层降低耦合:修改结果集映射逻辑,不会影响 Executor 层;
    • 每层依赖「接口」而非「实现」:比如 Executor 是接口,具体实现可替换;
    • 分层便于测试:可单独测试 ParameterHandler 的参数绑定逻辑。

2.2 插件化设计

MyBatis 提供了插件扩展机制(Interceptor),允许开发者通过拦截器增强核心组件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)的功能,实现如分页、日志、加密等。

  • 核心设计
    • 定义 Interceptor 接口,开发者实现 intercept 方法即可拦截目标方法;
    • 通过 @Intercepts 注解指定拦截的类和方法,无需修改源码;
    • 拦截器链(InterceptorChain)通过动态代理层层包装目标对象,保证插件的有序执行;
  • 架构思维
    • 「开闭原则」的良好体现:框架核心功能固定,扩展功能通过插件实现,无需修改源码;
    • 「责任链模式」:多个插件按顺序执行,每个插件只处理自己的逻辑,互不干扰;
    • 扩展点设计要「最小化」:只开放核心组件的关键方法,避免过度暴露内部逻辑。

2.3 多级缓存

MyBatis 实现了「一级缓存(SqlSession 级别)+ 二级缓存(Mapper 级别)」的多级缓存:

  • 一级缓存:BaseExecutor 中的 localCache(PerpetualCache),默认开启,SqlSession 关闭后失效;
  • 二级缓存:CachingExecutor 包装普通 Executor,缓存数据存入 Mapper 对应的 Cache 对象,跨 SqlSession 共享;

同时支持集成 Redis/Ehcache 等第三方缓存 。

2.4 面向接口编程

MyBatis 全程贯彻「依赖倒置原则」,完全面向接口编程:接口定义「做什么」,实现类定义「怎么做」,替换实现类不影响上层逻辑;

  • 核心组件都是接口:ExecutorStatementHandlerParameterHandlerResultSetHandlerSqlSession 等;
  • 上层代码只依赖接口:比如 SqlSessionselectList 方法,底层调用的是 Executor 接口的 query 方法,无需关心具体是 SimpleExecutor 还是 BatchExecutor。

2.5 约定优于配置

MyBatis 大量使用约定来减少配置,通过约定可以显著降低配置量,提升开发体验。例如:

  • Mapper 接口与 XML 文件同名同包;
  • 方法名与 SQL ID 一致;
  • 结果集字段与 JavaBean 属性自动映射;
  • StatementHandler接口默认使用PreparedStatementHandler

2.6 架构思维总结

MyBatis源码体现了简单而不简陋的设计哲学:

  1. 高内聚低耦合 - 模块职责清晰,依赖抽象
  2. 开闭原则 - 对扩展开放,对修改关闭
  3. 组合优于继承 - 装饰器、代理模式的应用
  4. 关注点分离 - SQL、映射、执行逻辑分离
  5. 性能优化 - 缓存、延迟加载、连接复用

上述技巧和思维不仅适用于框架开发,在日常业务开发中也能直接落地。

  1. 小型项目:学习其简洁的API设计
  2. 中型项目:借鉴其分层架构和模式应用
  3. 大型项目:参考其扩展机制和插件体系