当前位置:首页 > 技术 > 正文内容

MyBatis-Plus 核心源码解析

访客 技术 2026年8月15日 1

自动配置类 MybatisPlusAutoConfiguration

MyBatis-Plus 的自动配置核心在于 MybatisPlusAutoConfiguration 类,其通过 Spring Boot 的自动配置机制初始化 SqlSessionFactory 实例:

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);

    if (StringUtils.hasText(this.properties.getConfigLocation())) {
        factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }

    applyConfiguration(factory);

    if (this.properties.getConfigurationProperties() != null) {
        factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }

    if (!ObjectUtils.isEmpty(this.interceptors)) {
        factory.setPlugins(this.interceptors);
    }

    if (this.databaseIdProvider != null) {
        factory.setDatabaseIdProvider(this.databaseIdProvider);
    }

    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
        factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }

    if (this.properties.getTypeAliasesSuperType() != null) {
        factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
    }

    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
        factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }

    if (!ObjectUtils.isEmpty(this.typeHandlers)) {
        factory.setTypeHandlers(this.typeHandlers);
    }

    Resource[] mapperLocations = this.properties.resolveMapperLocations();
    if (!ObjectUtils.isEmpty(mapperLocations)) {
        factory.setMapperLocations(mapperLocations);
    }

    Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
    if (!ObjectUtils.isEmpty(this.languageDrivers)) {
        factory.setScriptingLanguageDrivers(this.languageDrivers);
    }

    Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);

    if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
        factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
    }

    GlobalConfig globalConfig = this.properties.getGlobalConfig();
    getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
    getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));
    getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
    getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);

    factory.setGlobalConfig(globalConfig);
    return factory.getObject();
}

SQL 注入核心 AbstractSqlInjector

MyBatis-Plus 通过 AbstractSqlInjector 抽象类来完成 SQL 方法的注入。主要逻辑如下:

public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
    Class<?> modelClass = extractModelClass(mapperClass);
    if (modelClass != null) {
        String className = mapperClass.toString();
        Set<String> mapperRegistryCache = GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());

        if (!mapperRegistryCache.contains(className)) {
            List<AbstractMethod> methodList = getMethodList(mapperClass);
            if (CollectionUtils.isNotEmpty(methodList)) {
                TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant, modelClass);
                methodList.forEach(m -> m.inject(builderAssistant, mapperClass, modelClass, tableInfo));
            } else {
                logger.debug(mapperClass.toString() + ", 未发现有效注入方法。");
            }
            mapperRegistryCache.add(className);
        }
    }
}

执行器管理 MybatisConfiguration

MyBatis-Plus 中对 MyBatis 的执行器进行了扩展,支持多种执行策略:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;

    Executor executor;
    if (ExecutorType.BATCH == executorType) {
        executor = new MybatisBatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
        executor = new MybatisReuseExecutor(this, transaction);
    } else {
        executor = new MybatisSimpleExecutor(this, transaction);
    }

    if (cacheEnabled) {
        executor = new MybatisCachingExecutor(executor);
    }

    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
}
  • SimpleExecutor:每次执行 SQL 都新建并关闭 Statement。
  • ReuseExecutor:复用 Statement 对象,避免重复创建。
  • BatchExecutor:批量执行 SQL,适合大批量更新操作。
  • CachingExecutor:启用二级缓存,优先从缓存读取数据。

这些执行器的作用范围均限定在 SqlSession 生命周期内。

Mapper 动态代理 MybatisMapperProxy

MyBatis-Plus 使用 JDK 动态代理机制为 Mapper 接口生成代理对象:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }

        if (method.isDefault()) {
            if (privateLookupInMethod == null) {
                return invokeDefaultMethodJava8(proxy, method, args);
            }
            return invokeDefaultMethodJava9(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }

    MybatisMapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
}

SqlSession 模板 SqlSessionTemplate

为了线程安全地使用 SqlSession,MyBatis-Plus 提供了 SqlSessionTemplate,其内部通过代理拦截方法调用:

private class SqlSessionInterceptor implements InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        SqlSession sqlSession = SqlSessionUtils.getSqlSession(
            sqlSessionFactory, executorType, exceptionTranslator);

        try {
            Object result = method.invoke(sqlSession, args);
            if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, sqlSessionFactory)) {
                sqlSession.commit(true);
            }
            return result;
        } catch (Throwable t) {
            Throwable unwrapped = ExceptionUtil.unwrapThrowable(t);
            if (exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
                sqlSession = null;
                Throwable translated = exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
                if (translated != null) {
                    unwrapped = translated;
                }
            }
            throw unwrapped;
        } finally {
            if (sqlSession != null) {
                SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
            }
        }
    }
}

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。