无处生还
76.43M · 2026-04-23
通过以下系列博文,我们熟悉了Mybatis源码实现的诸多细节,如Executor接口及其实现、缓存体系、自定义插件等。
这次,让我们跳出局部,看看Mybatis在设计和架构上,有哪些值得我们学习的地方。
MyBatis 大量使用建造者模式解决「复杂对象初始化」问题,比如 SqlSessionFactoryBuilder、XMLConfigBuilder、MapperBuilderAssistant 等。
以SqlSessionFactoryBuilder为例,在创建SqlSessionFactory前需要先解析主配置文件,这个过程非常繁琐。使用建造者模式:
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());
}
}
工厂模式隐藏对象创建细节,上层无需关心「具体实现类」,只依赖接口。例如
SqlSessionFactory 负责创建 SqlSession,提供了多个重载实现。Configuration 中的 newExecutor 等方法创建,根据配置选择不同实现类;同时应用自定义插件。MyBatis 核心的特性之一是「Mapper 接口无需实现类」,底层通过 JDK 动态代理,实现接口方法调用触发SQL执行。详见 MapperProxyFactory,缓存方法元数据,避免重复解析。
// 缓存mapper方法元数据,避免重复解析
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
此外,插件原理也是动态代理,定义Interceptor接口声明代理逻辑、代理对象创建等。使用户可以自定义插件实现。
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(...)
用装饰模式替代继承,无需定义子类,就能给对象动态增加职责。
如通过CachingExecutor装饰,给BaseExecutor增加二级缓存能力。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// 对BaseExecutor子类进行装饰
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 省略...
}
如Cache接口体系,定义了很多装饰器,根据用户配置,实现功能特性自由组合。
Executor接口可根据用户配置,运行时可切换SimpleExecutor、ReuseExecutor或BatchExecutor,实现不同的SQL执行策略。
RoutingStatementHandler 根据配置路由到不同的 StatementHandler实现。
源码中大量使用 Registry 模式来管理可扩展组件, 可以统一初始化、统一查找、统一生命周期;例如:
MyBatis的核心分层非常清晰,每层只做一件事,符合「单一职责原则」:
MyBatis 提供了插件扩展机制(Interceptor),允许开发者通过拦截器增强核心组件(Executor、StatementHandler、ParameterHandler、ResultSetHandler)的功能,实现如分页、日志、加密等。
Interceptor 接口,开发者实现 intercept 方法即可拦截目标方法;@Intercepts 注解指定拦截的类和方法,无需修改源码;MyBatis 实现了「一级缓存(SqlSession 级别)+ 二级缓存(Mapper 级别)」的多级缓存:
localCache(PerpetualCache),默认开启,SqlSession 关闭后失效;同时支持集成 Redis/Ehcache 等第三方缓存 。
MyBatis 全程贯彻「依赖倒置原则」,完全面向接口编程:接口定义「做什么」,实现类定义「怎么做」,替换实现类不影响上层逻辑;
Executor、StatementHandler、ParameterHandler、ResultSetHandler、SqlSession 等;SqlSession 的 selectList 方法,底层调用的是 Executor 接口的 query 方法,无需关心具体是 SimpleExecutor 还是 BatchExecutor。MyBatis 大量使用约定来减少配置,通过约定可以显著降低配置量,提升开发体验。例如:
StatementHandler接口默认使用PreparedStatementHandlerMyBatis源码体现了简单而不简陋的设计哲学:
上述技巧和思维不仅适用于框架开发,在日常业务开发中也能直接落地。