【大白话说Java面试题 第134题】【05_Mybatis篇】第4题:MyBatis 源码中用了哪些设计模式?为什么要用这些设计模式?

📌 PDF :大白话说Java面试题 --- 05_Mybatis篇

第4题:MyBatis 源码中用了哪些设计模式?为什么要用这些设计模式?

📚 回答:

  • 核心考点 : MyBatis 源码中设计模式的应用不是"背出几个模式名字"就能过关的。大厂面试中,面试官期望你深入理解 每个设计模式在 MyBatis 中的具体落地 (不是泛泛而谈,而是能说出类名、方法名、调用链路)、多种设计模式的协同工作 (如 Executor 创建过程中装饰器 + 策略 + 模板方法 + 代理的叠加),以及 设计模式选择的工程权衡(为什么这里用装饰器而非继承、为什么插件用责任链而非 AOP)。面试官真正想判断的是:你是否具备从优秀开源框架中学习架构设计的能力。

1. 构建者模式(Builder Pattern):复杂对象的渐进式构建
  • 1.1 应用场景:SqlSessionFactory 的创建 SqlSessionFactory 的创建涉及 XML 解析、配置初始化、Mapper 注册等复杂步骤,MyBatis 使用构建者模式将过程拆解:
java 复制代码
// 典型使用:三步构建
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
  • 1.2 源码解析:分阶段构建 构建者模式的核心是将复杂对象的构建过程与其表示分离
java 复制代码
public class SqlSessionFactoryBuilder {
    // 阶段1:从 InputStream 解析 XML
    public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
    }

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // XMLConfigBuilder 是具体构建器:解析配置文件
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 阶段2:生成 Configuration(产品的一部分)
            Configuration config = parser.parse();
            // 阶段3:用 Configuration 构建最终产品
            return build(config);
        } finally {
            inputStream.close();
        }
    }

    // 阶段3:从 Configuration 构建 SqlSessionFactory
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}
角色 职责
Director(导演) SqlSessionFactoryBuilder.build() 控制构建步骤的顺序
Builder(抽象构建器) BaseBuilder 定义构建接口
ConcreteBuilder(具体构建器) XMLConfigBuilder / XMLMapperBuilder 解析 XML,填充 Configuration
Product(产品) SqlSessionFactory / Configuration 最终构建的复杂对象
  • 1.3 为什么用构建者模式? | 不用构建者模式 | 使用构建者模式 | | ------------ | ------------ | | 构造函数参数爆炸(10+ 个参数) | 分阶段设置,参数清晰 | | 构建顺序混乱,易出错 | 导演控制顺序,保证一致性 | | 无法支持多种配置源 | 支持 XML、注解、Java Config 多种构建方式 | | 构建逻辑与产品耦合 | 构建逻辑独立,产品类更纯粹 |

2. 工厂模式(Factory Pattern):对象的统一创建
  • 2.1 应用场景:SqlSession 和 Executor 的创建 MyBatis 使用工厂模式统一创建核心对象,隐藏实现细节:
java 复制代码
// SqlSessionFactory 是工厂接口
public interface SqlSessionFactory {
    SqlSession openSession();                    // 创建 SqlSession
    SqlSession openSession(boolean autoCommit);  // 带参数的创建
    SqlSession openSession(Connection connection);
}

// DefaultSqlSessionFactory 是具体工厂
public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;

    @Override
    public SqlSession openSession() {
        return openSessionFromDataSource(
            configuration.getDefaultExecutorType(),  // 获取默认执行器类型
            null, 
            false
        );
    }

    private SqlSession openSessionFromDataSource(ExecutorType execType, 
                                                  TransactionIsolationLevel level, 
                                                  boolean autoCommit) {
        // 1. 从 Environment 获取数据源
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = environment.getTransactionFactory();
        // 2. 创建事务
        final Transaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // 3. 创建 Executor(工厂模式再次应用)
        final Executor executor = configuration.newExecutor(tx, execType);
        // 4. 创建 SqlSession
        return new DefaultSqlSession(configuration, executor, autoCommit);
    }
}
  • 2.2 Executor 的工厂创建 Configuration 作为工厂,根据 ExecutorType 创建不同的 Executor:
java 复制代码
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    Executor executor;
    // 策略模式:根据类型创建不同实现
    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);
    }
    // 代理模式:插件拦截
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}
Executor 类型 特点 适用场景
SimpleExecutor 每次执行新建 Statement 通用场景
ReuseExecutor 复用 Statement(Map 缓存) 同 SQL 多次执行
BatchExecutor 批量缓存,统一提交 批量插入/更新
CachingExecutor 装饰器,添加二级缓存 读多写少

3. 动态代理模式(Dynamic Proxy Pattern):Mapper 接口的实现生成
  • 3.1 应用场景:Mapper 接口无需实现类 开发者只定义接口,MyBatis 通过 JDK 动态代理自动生成实现:
java 复制代码
// 使用:像调用普通方法一样执行 SQL
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectById(1);
  • 3.2 源码解析:MapperProxy 的拦截链路 详细链路见第2题,核心类关系:

    MapperRegistry(注册中心)


    MapperProxyFactory(代理工厂)


    MapperProxy implements InvocationHandler(方法拦截)


    MapperMethod(SQL 执行分发)


    SqlSession → Executor → JDBC

  • 3.3 为什么用动态代理而非直接生成类? | 方案 | 优点 | 缺点 | | ---- | ---- | ---- | | 动态代理 | 无侵入,接口即契约 | 只能代理接口方法 | | 直接生成类(CGLIB) | 可代理类的方法 | 需引入第三方库,字节码操作复杂 | | 静态代码生成 | 运行时无代理开销 | 编译期生成,灵活性差 |

MyBatis 选择 JDK 动态代理的原因:Mapper 本身就是接口,JDK 代理天然适合;无额外依赖;代理范围精准(只代理接口方法,Object 方法过滤)。


4. 装饰器模式(Decorator Pattern):功能的动态叠加
  • 4.1 应用场景:Executor 和 Cache 的功能增强 装饰器模式是 MyBatis 中最核心的设计模式之一,用于在不修改原有类的情况下动态添加功能

  • 4.2 Executor 的装饰器:CachingExecutor CachingExecutorExecutor 添加二级缓存功能:

java 复制代码
public class CachingExecutor implements Executor {
    private final Executor delegate;  // 被装饰的 Executor

    public CachingExecutor(Executor delegate) {
        this.delegate = delegate;
    }

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, 
                             RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 1. 先查二级缓存
        Cache cache = ms.getCache();
        if (cache != null) {
            List<E> list = (List<E>) cache.getObject(cacheKey);
            if (list != null) return list;  // 缓存命中
        }
        // 2. 缓存未命中,委托给被装饰的 Executor
        return delegate.query(ms, parameter, rowBounds, resultHandler);
    }

    @Override
    public int update(MappedStatement ms, Object parameter) throws SQLException {
        // 更新操作先清空缓存
        flushCacheIfRequired(ms);
        // 委托给被装饰的 Executor
        return delegate.update(ms, parameter);
    }
}
  • 4.3 Cache 的装饰器链 二级缓存通过多层装饰器实现功能叠加:
java 复制代码
// CacheBuilder 构建装饰器链
public Cache build() {
    // 1. 基础缓存
    setDefaultImplementations();
    Cache cache = newBaseCacheInstance(implementation, id);
    // 2. 设置属性
    setCacheProperties(cache);
    // 3. 装饰器层层包装
    if (PerpetualCache.class.equals(cache.getClass())) {
        for (Class<? extends Cache> decorator : decorators) {
            cache = newCacheDecoratorInstance(decorator, cache);
            setCacheProperties(cache);
        }
        // 标准装饰器链
        cache = setStandardDecorators(cache);
    }
    return cache;
}

private Cache setStandardDecorators(Cache cache) {
    try {
        // 装饰器1:序列化(对象隔离)
        cache = new SerializedCache(cache);
        // 装饰器2:日志(命中率统计)
        cache = new LoggingCache(cache);
        // 装饰器3:同步(线程安全)
        cache = new SynchronizedCache(cache);
        // 装饰器4:阻塞(防缓存击穿)
        if (blocking) {
            cache = new BlockingCache(cache);
        }
    } catch (Exception e) {
        throw new CacheException("Error building standard cache decorators.  Cause: " + e, e);
    }
    return cache;
}
装饰器 功能 被装饰对象
SerializedCache 序列化隔离 Cache
LoggingCache 命中率统计 Cache
SynchronizedCache 线程安全 Cache
BlockingCache 防缓存击穿 Cache
CachingExecutor 二级缓存 Executor
  • 4.4 为什么用装饰器而非继承? | 继承 | 装饰器 | | ---- | ------ | | 静态绑定,编译期确定功能 | 动态组合,运行期叠加功能 | | 功能叠加导致类爆炸(N个功能需2^N个子类) | 功能独立,任意组合 | | 修改父类影响所有子类 | 被装饰类无感知 | | 不符合开闭原则 | 符合开闭原则(扩展开放,修改关闭) |

5. 模板方法模式(Template Method Pattern):流程标准化,细节定制化
  • 5.1 应用场景:BaseExecutor 定义查询/更新流程 BaseExecutor 定义了 SQL 执行的标准流程,子类只实现具体步骤:
java 复制代码
public abstract class BaseExecutor implements Executor {
    // 模板方法:定义查询的标准流程
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, 
                             RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
        // 1. 获取 BoundSql(标准步骤)
        BoundSql boundSql = ms.getBoundSql(parameter);
        // 2. 创建 CacheKey(标准步骤)
        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 {
        // 4. 检查一级缓存(标准步骤)
        if (queryStack == 0 && ms.isFlushCacheRequired()) {
            clearLocalCache();
        }
        List<E> list;
        try {
            queryStack++;
            // 5. 查一级缓存(标准步骤)
            list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
            if (list != null) {
                handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
            } else {
                // 6. 缓存未命中,调用抽象方法(子类实现)
                list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
            }
        } finally {
            queryStack--;
        }
        return list;
    }

    // 抽象方法:子类必须实现(钩子方法)
    protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, 
                                           RowBounds rowBounds, ResultHandler resultHandler, 
                                           BoundSql boundSql) throws SQLException;

    // 模板方法中的具体步骤
    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 {
            // 调用子类实现的抽象方法
            list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            localCache.removeObject(key);
        }
        localCache.putObject(key, list);
        return list;
    }
}
  • 5.2 模板方法的标准流程 1. 获取 BoundSql(标准) 2. 创建 CacheKey(标准) 3. 检查一级缓存(标准) 4. 缓存命中 → 返回结果(标准) 5. 缓存未命中 → 调用 doQuery()(子类实现) 6. 结果放入缓存(标准)

| 子类 | 实现的 doQuery 特点 | | ---- | ------------------- | | SimpleExecutor | 每次新建 Statement | | ReuseExecutor | 复用 Statement | | BatchExecutor | 批量缓存 Statement |

  • 5.3 BaseTypeHandler 也是模板方法 类型处理器使用模板方法定义处理流程:
java 复制代码
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
    // 模板方法:定义设置参数的标准流程
    @Override
    public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if (parameter == null) {
            // 标准步骤:处理 null
            setNull(ps, i, jdbcType);
        } else {
            // 钩子方法:子类实现非 null 的处理
            setNonNullParameter(ps, i, parameter, jdbcType);
        }
    }

    // 抽象方法:子类必须实现
    public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
}

6. 策略模式(Strategy Pattern):算法的可插拔替换
  • 6.1 应用场景:Executor 类型选择和 StatementHandler 路由 策略模式定义一族算法,让它们可以互相替换:
java 复制代码
// ExecutorType 枚举定义策略
public enum ExecutorType {
    SIMPLE,      // 简单执行策略
    REUSE,       // 复用 Statement 策略
    BATCH        // 批量执行策略
}

// Configuration 作为上下文,选择策略
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    Executor executor;
    // 策略选择
    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);
    }
    // ...
}
  • 6.2 RoutingStatementHandler:策略路由 根据 SQL 类型路由到不同的 StatementHandler
java 复制代码
public class RoutingStatementHandler implements StatementHandler {
    private final StatementHandler delegate;

    public RoutingStatementHandler(Executor executor, MappedStatement ms, 
                                    Object parameter, RowBounds rowBounds,
                                    ResultHandler resultHandler, BoundSql boundSql) {
        // 根据 SQL 类型选择策略
        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);
    }
}

| 策略 | 适用场景 | | ---- | -------- | | SimpleStatementHandler | 静态 SQL,无参数 | | PreparedStatementHandler | 预编译 SQL,有参数(最常用) | | CallableStatementHandler | 存储过程调用 |


7. 责任链模式(Chain of Responsibility Pattern):插件的链式拦截
  • 7.1 应用场景:InterceptorChain 插件机制 MyBatis 插件通过责任链模式实现多层拦截
java 复制代码
public class InterceptorChain {
    private final List<Interceptor> interceptors = new ArrayList<>();

    // 添加拦截器
    public void addInterceptor(Interceptor interceptor) {
        interceptors.add(interceptor);
    }

    // 责任链:对所有目标对象应用所有拦截器
    public Object pluginAll(Object target) {
        for (Interceptor interceptor : interceptors) {
            target = interceptor.plugin(target);  // 层层代理
        }
        return target;
    }
}
  • 7.2 插件的代理叠加 每个插件通过 Plugin.wrap() 生成代理对象,多个插件形成代理链
java 复制代码
// 插件1:分页插件
@Intercepts({
    @Signature(type = Executor.class, method = "query", 
               args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class PageInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 1. 处理分页逻辑
        // 2. 调用下一个拦截器(或目标方法)
        return invocation.proceed();
    }
}

// 插件2:SQL 性能监控
@Intercepts({
    @Signature(type = Executor.class, method = "query", args = {...})
})
public class PerformanceInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        long start = System.currentTimeMillis();
        Object result = invocation.proceed();  // 调用下一个
        long cost = System.currentTimeMillis() - start;
        System.out.println("SQL cost: " + cost + "ms");
        return result;
    }
}
  • 7.3 代理链的执行顺序 调用者 → 插件1代理 → 插件2代理 → 原始 Executor → 插件2代理 → 插件1代理 → 调用者 每个插件可以在 proceed() 前后添加前置/后置逻辑。

  • 7.4 为什么用责任链而非 Spring AOP? | 责任链 | Spring AOP | | ------ | ---------- | | 框架内置,无额外依赖 | 需引入 Spring | | 精准拦截 MyBatis 四大对象 | 通用性强,但配置复杂 | | 插件可独立发布(如 PageHelper) | 与 Spring 强耦合 | | 代理粒度到方法签名级别 | 基于切点表达式 |


8. 设计模式的协同:Executor 创建全过程

MyBatis 中多种设计模式协同工作 ,以 Configuration.newExecutor() 为例:

java 复制代码
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // 1. 策略模式:根据类型选择 Executor 实现
    Executor executor;
    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);
    }

    // 2. 装饰器模式:添加二级缓存功能
    if (cacheEnabled) {
        executor = new CachingExecutor(executor);
    }

    // 3. 代理模式(责任链):应用插件拦截
    executor = (Executor) interceptorChain.pluginAll(executor);

    return executor;
}

| 步骤 | 设计模式 | 作用 | | ---- | -------- | ---- | | 选择 Executor 实现 | 策略模式 | 根据配置切换算法 | | 包装 CachingExecutor | 装饰器模式 | 动态添加缓存功能 | | 应用插件 | 代理模式 + 责任链 | 多层拦截增强 | | 最终执行 | 模板方法模式 | 标准化执行流程 |


9. 其他设计模式

| 设计模式 | 应用场景 | 具体类 | | -------- | -------- | ------ | | 单例模式 | ConfigurationErrorContext | 全局唯一配置 | | 适配器模式 | Log 接口适配多种日志框架 | Slf4jImplLog4jImpl | | 组合模式 | SqlNode(动态 SQL 解析) | MixedSqlNodeIfSqlNode | | 迭代器模式 | PropertyTokenizer | 属性名分词迭代 | | 外观模式 | SqlSession | 简化数据库操作接口 | | 享元模式 | SqlSource 缓存 | 复用解析后的 SQL | |


10. 面试官追问与高分回答模板
  • 追问 1:"MyBatis 源码中用了哪些设计模式?"

    低分回答:"工厂模式、代理模式、装饰器模式等。"(没有具体落地)

    高分回答

    "MyBatis 源码中至少使用了 8 种设计模式,每种都有明确的落地:

    1. 构建者模式SqlSessionFactoryBuilder 分阶段构建 SqlSessionFactoryXMLConfigBuilder 解析 XML 填充 Configuration
    2. 工厂模式SqlSessionFactory.openSession() 创建 SqlSessionConfiguration.newExecutor() 根据 ExecutorType 创建不同的 Executor。
    3. 动态代理模式MapperProxy 实现 InvocationHandler,为 Mapper 接口生成代理对象,拦截方法调用映射到 SQL。
    4. 装饰器模式CachingExecutor 装饰 Executor 添加二级缓存,SerializedCache/LoggingCache 等装饰 PerpetualCache
    5. 模板方法模式BaseExecutor 定义查询/更新的标准流程(缓存检查 → 执行 → 结果缓存),doQuery()/doUpdate() 由子类实现。
    6. 策略模式ExecutorType 枚举选择 SimpleExecutor/ReuseExecutor/BatchExecutorRoutingStatementHandler 根据 SQL 类型路由。
    7. 责任链模式InterceptorChain 管理插件,pluginAll() 层层代理形成拦截链。
    8. 单例模式Configuration 全局唯一,存储所有配置信息。

    这些模式不是孤立使用的,而是协同工作。例如 Configuration.newExecutor() 中,先用策略模式 选择 Executor,再用装饰器模式 包装 CachingExecutor,最后用代理模式应用插件。"

  • 追问 2:"为什么 Executor 用装饰器模式而非继承?"

    高分回答

    "MyBatis 选择装饰器模式而非继承,核心原因是避免类爆炸和满足开闭原则

    1. 类爆炸问题:如果用继承,缓存功能、日志功能、同步功能需要排列组合。3 个功能就需要 2^3 = 8 个子类,N 个功能需要 2^N 个子类,不可维护。
    2. 动态组合 :装饰器模式允许运行期动态叠加功能。CachingExecutor 可以装饰 SimpleExecutor,也可以装饰 ReuseExecutor,组合灵活。
    3. 开闭原则 :新增功能只需新增装饰器类,不修改原有 Executor 代码。例如新增 BlockingCache 只需实现 Cache 接口并包装现有实现。
    4. 职责单一 :每个装饰器只负责一个功能(如 SerializedCache 只负责序列化),符合单一职责原则。

    源码中 CacheBuilder.setStandardDecorators() 就是典型应用:依次包装 SerializedCacheLoggingCacheSynchronizedCache,层层叠加。"

  • 追问 3:"MyBatis 的插件机制是怎么实现的?为什么用责任链模式?"

    高分回答

    "MyBatis 插件机制基于责任链模式 + JDK 动态代理实现:

    1. 注册阶段 :解析 mybatis-config.xml 中的 <plugins>,将拦截器加入 InterceptorChain.interceptors 列表。
    2. 代理创建阶段Configuration.newExecutor() 创建 Executor 后,调用 interceptorChain.pluginAll(executor),遍历所有拦截器,每个拦截器通过 Plugin.wrap() 生成代理对象。
    3. 执行阶段 :调用代理对象方法时,Plugin.invoke() 检查方法签名是否匹配 @Intercepts 注解,匹配则调用 Interceptor.intercept(),否则直接调用目标方法。

    为什么用责任链而非 Spring AOP?

    • MyBatis 插件是框架内置能力,无需引入 Spring,减少依赖。
    • 插件可独立发布(如 PageHelper),用户按需引入。
    • 代理粒度精准到方法签名(type + method + args),AOP 的切点表达式更通用但配置复杂。
    • 责任链的执行顺序由配置顺序决定,可控且直观。"
  • 追问 4:"模板方法模式在 BaseExecutor 中是怎么体现的?"

    高分回答

    "BaseExecutor 是模板方法模式的经典应用,它定义了 SQL 查询的标准流程 (模板方法 query()),将具体执行延迟到子类(doQuery()):

    模板方法 query() 的标准流程

    1. 获取 BoundSql
    2. 创建 CacheKey
    3. 检查一级缓存(localCache.getObject(key)
    4. 缓存命中 → 返回结果
    5. 缓存未命中 → 调用 queryFromDatabase()
    6. queryFromDatabase() 中调用抽象方法 doQuery()(子类实现)
    7. 结果放入一级缓存

    子类实现的差异

    • SimpleExecutor.doQuery():每次新建 StatementHandler → 新建 Statement → 执行
    • ReuseExecutor.doQuery():从 Map<String, Statement> 复用 Statement
    • BatchExecutor.doUpdate():将 SQL 加入批处理队列,统一提交

    这种设计把公共流程 (缓存、事务)和可变逻辑(Statement 创建方式)分离,避免代码重复。"

  • 追问 5:"构建者模式和工厂模式在 MyBatis 中有什么区别?"

    高分回答

    "两者都用于对象创建,但关注点不同:

    维度 构建者模式 工厂模式
    关注点 复杂对象的分阶段构建 对象的统一创建入口
    典型类 SqlSessionFactoryBuilder SqlSessionFactoryConfiguration
    产品复杂度 高(需多步骤组装) 中等(根据参数创建不同实现)
    调用方式 链式调用或分步调用 简单工厂方法

    具体例子

    • 构建者模式SqlSessionFactoryBuilder.build(inputStream) 分三步:解析 XML → 生成 Configuration → 创建 DefaultSqlSessionFactory。每一步都依赖前一步的结果。
    • 工厂模式SqlSessionFactory.openSession() 直接返回 SqlSession,隐藏了 TransactionExecutor 的创建细节。Configuration.newExecutor() 根据 ExecutorType 返回不同的 Executor 实现。

    简单说:构建者模式解决怎么一步一步构建 ,工厂模式解决创建哪个实现类。"

  • 追问 6:"如果从零设计一个 ORM 框架,你会怎么选择设计模式?"

    高分回答

    "我会参考 MyBatis 的设计,根据需求做取舍:

    1. 构建者模式 :配置解析和核心对象创建必须用,如 SessionFactoryBuilder,保证复杂对象的正确构建。
    2. 动态代理模式:Mapper 接口代理必选,JDK 代理足够,除非需要代理类才考虑 CGLIB。
    3. 装饰器模式:缓存、日志、监控等功能用装饰器叠加,避免继承爆炸。
    4. 模板方法模式:定义 SQL 执行的标准流程(如查询:缓存检查 → 执行 → 结果映射),子类实现数据库方言差异。
    5. 策略模式:支持多种执行策略(简单、批处理、流式查询),支持多种数据库方言(MySQL、PostgreSQL、Oracle)。
    6. 责任链模式:插件机制用责任链,但会限制插件数量(代理层数过多影响性能)。
    7. 事件驱动模式:考虑用观察者模式替代部分插件功能,降低代理开销。

    关键权衡

    • 性能敏感场景减少装饰器和代理层数
    • 插件机制提供但限制使用场景,避免过度设计
    • 类型转换用策略模式而非大量 if-else"

11. 方案选型速查表
设计模式 MyBatis 落地类 解决的问题 替代方案及不适用原因
构建者模式 SqlSessionFactoryBuilder 复杂对象分阶段构建 构造函数:参数过多;工厂:不关注构建过程
工厂模式 DefaultSqlSessionFactory 统一创建入口 直接 new:创建逻辑暴露
动态代理 MapperProxy 接口实现自动生成 静态代理:每个接口需手写;CGLIB:过度设计
装饰器模式 CachingExecutorSerializedCache 功能动态叠加 继承:类爆炸;AOP:框架过重
模板方法 BaseExecutorBaseTypeHandler 流程标准化 策略模式:无法定义固定流程
策略模式 ExecutorTypeRoutingStatementHandler 算法可插拔 枚举+if-else:扩展性差
责任链 InterceptorChain 多层拦截 AOP:引入外部框架;观察者:无法顺序控制
单例模式 Configuration 全局唯一配置 静态类:无法 mock 测试

💡 面试官想要的满分总结

MyBatis 源码是设计模式的教科书级实践 ,不是简单堆砌模式,而是根据问题特征选择最合适的模式,并让多种模式协同工作

理解 MyBatis 的设计模式必须抓住三个核心洞察:

  1. 装饰器模式是架构核心CachingExecutor 装饰 ExecutorSerializedCache 装饰 PerpetualCache,用组合替代继承,避免类爆炸,符合开闭原则。这是 MyBatis 扩展性最强的设计。
  2. 模板方法定义框架骨架BaseExecutor 定义查询/更新的标准流程(缓存检查 → 执行 → 结果缓存),doQuery() 由子类实现,把公共流程和可变逻辑精准分离。
  3. 多种模式协同而非孤立Configuration.newExecutor() 是最佳案例------策略模式选择实现 → 装饰器模式添加缓存 → 代理模式应用插件 → 模板方法模式定义执行流程。

工程启示:设计模式不是炫技,而是解决特定问题的标准方案。MyBatis 的选择告诉我们------当需要动态叠加功能时用装饰器,当需要标准化流程时用模板方法,当需要算法替换时用策略模式。真正的高手不是记住多少模式,而是能根据问题特征快速匹配最合适的模式,并理解模式之间的协作关系。


觉得对您有帮助,麻烦 点点关注啦 ,您的关注是我创作的最大动力~ 🎯