【mybatis和mybatis-plus】源码分析

mybatis

核心类和接口说明

Environment

环境配置,包含id、TransactionFactory(事务工厂)、DataSource

【mybatis和mybatis-plus】源码分析_第1张图片

TransactionFactory有三个实现类,我们与spring整合,默认使用第三个事务工厂
【mybatis和mybatis-plus】源码分析_第2张图片

TypeAliasRegistry

别名映射

【mybatis和mybatis-plus】源码分析_第3张图片
比如

全限定名:parameterType="java.lang.Integer"
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
别名:parameterType="integer"
<select id="selectByPrimaryKey" parameterType="integer" resultMap="BaseResultMap">

TypeHandlerRegistry

jdbc与java的类型映射处理器

【mybatis和mybatis-plus】源码分析_第4张图片
比如StringTypeHandler,映射java的String,对应数据库VARCHAR
【mybatis和mybatis-plus】源码分析_第5张图片

BaseBuilder

抽象类
字段包括:Configuration、TypeAliasRegistry、TypeHandlerRegistry

【mybatis和mybatis-plus】源码分析_第6张图片

XMLMapperBuilder

mapper构造器,一个mapper对应一个XMLMapperBuilder类,继承父类BaseBuilder
变量包括XPathParser、MapperBuilderAssistant、sqlFragments、mapper文件resource

【mybatis和mybatis-plus】源码分析_第7张图片
在构造方法中,初始化了XPathParser、MapperBuilderAssistant(mapper缓存处理类)

【mybatis和mybatis-plus】源码分析_第8张图片

MapperBuilderAssistant

mapper构造器助手,主要用来处理二级缓存的逻辑、构造MappedStatement对象,继承父类BaseBuilder
变量包括:当前namespace、文件resource、Cache(当前二级缓存)、unresolvedCacheRef(二级缓存CacheRef),其它属性是通用配置

【mybatis和mybatis-plus】源码分析_第9张图片
变量包括:当前namespace、文件resource、当前二级缓存、unresolvedCacheRef,其它属性是通用配置
【mybatis和mybatis-plus】源码分析_第10张图片

XMLStatementBuilder

Statment构造器,用于解析继承父类BaseBuilder

XMLScriptBuilder

xml中表达式构造器,用于添加动态sql对应的处理器、生成SqlSource对象

【mybatis和mybatis-plus】源码分析_第11张图片
【mybatis和mybatis-plus】源码分析_第12张图片

XMLLanguageDriver

这里是引用

XPathParser

基于sax解析xml并封装document对象

【mybatis和mybatis-plus】源码分析_第13张图片

SqlSessionTemplate

基于jdk动态代理,在构造方法中创建SqlSession的代理类,代理主要是为了统一处理sqlSession方法执行前后的事务功能

【mybatis和mybatis-plus】源码分析_第14张图片

构造方法
【mybatis和mybatis-plus】源码分析_第15张图片

SqlSessionInterceptor

作为SqlSessionTemplate的内部类,提供了SqlSessionTemplate动态代理SqlSession的InvocationHandler实现

【mybatis和mybatis-plus】源码分析_第16张图片

源码

SqlSessionFactoryBean -> buildSqlSessionFactory -> XMLMapperBuilder.parse()

解析mapper文件资源

【mybatis和mybatis-plus】源码分析_第17张图片
【mybatis和mybatis-plus】源码分析_第18张图片

configurationElement

传入参数为XNode,由parser.evalNode(“/mapper”)方法返回结果
【mybatis和mybatis-plus】源码分析_第19张图片

buildStatementFromContext(context.evalNodes(“select|insert|update|delete”))

【mybatis和mybatis-plus】源码分析_第20张图片
解析出来的集合内容如下,包含我们写的sql 原始内容
【mybatis和mybatis-plus】源码分析_第21张图片
遍历XNode,通过XMLStatementBuilder.parseStatementNode()解析每一个XNode
【mybatis和mybatis-plus】源码分析_第22张图片

XMLStatementBuilder.parseStatementNode()

【mybatis和mybatis-plus】源码分析_第23张图片

LanguageDriver.createSqlSource(configuration, context, parameterTypeClass);

通过XMLLanguageDriver.createSqlSource,返回SqlSource。核心由XMLScriptBuilder来代替实现
【mybatis和mybatis-plus】源码分析_第24张图片
XMLScriptBuilder.parseScriptNode

  1. 先通过parseDynamicTags用于解析XNode的内容,通过不同的类型(动态标签,普通文本等)返回sqlNode集合
    【mybatis和mybatis-plus】源码分析_第25张图片
    通过缓存的nodeHandlerMap,获取动态标签的值对应的处理器(如 => WhereHandler )
    【mybatis和mybatis-plus】源码分析_第26张图片
    将处理结果添加到集合中
    【mybatis和mybatis-plus】源码分析_第27张图片
    最终返回的集合包含了StaticTextSqlNode、WhereSqlNode,将集合封装到MixedSqlNode并返回
    原始sql
 <select id="normalList" resultType="org.hopehomi.cloud.entity.Orders" parameterType="integer">
        select * from orders
        <where>
            id = #{count}
        </where>
    </select>

【mybatis和mybatis-plus】源码分析_第28张图片
2. 接下来判断XNode是否包含动态sql标签
如果包含动态sql标签,封装成DynamicSqlSource并返回
【mybatis和mybatis-plus】源码分析_第29张图片

MapperBuilderAssistant.addMappedStatement(…)

【mybatis和mybatis-plus】源码分析_第30张图片
【mybatis和mybatis-plus】源码分析_第31张图片
【mybatis和mybatis-plus】源码分析_第32张图片

通过建造者模式,构造出MappedStatement,缓存到Configuration中
key为MappedStatement的id(namespace + “.” + id)

【mybatis和mybatis-plus】源码分析_第33张图片
【mybatis和mybatis-plus】源码分析_第34张图片

【mybatis和mybatis-plus】源码分析_第35张图片

bindMapperForNamespace

【mybatis和mybatis-plus】源码分析_第36张图片
通过namespace获取到mapper接口,执行configuration.addMapper(boundType);
【mybatis和mybatis-plus】源码分析_第37张图片
由MapperRegistry来执行

MapperRegistry.addMapper

【mybatis和mybatis-plus】源码分析_第38张图片

添加Mapper注解映射,比如@Select、@Update

MapperAnnotationBuilder的构造方法

【mybatis和mybatis-plus】源码分析_第39张图片

【mybatis和mybatis-plus】源码分析_第40张图片

MybatisMapperAnnotationBuilder.parse()

判断方法是否通过注解的方式执行
【mybatis和mybatis-plus】源码分析_第41张图片
【mybatis和mybatis-plus】源码分析_第42张图片

SqlSessionFactoryBean -> buildSqlSessionFactory -> this.sqlSessionFactoryBuilder.build(targetConfiguration)

【mybatis和mybatis-plus】源码分析_第43张图片
返回SqlSessionFactory的默认实现DefaultSqlSessionFactory

【mybatis和mybatis-plus】源码分析_第44张图片
【mybatis和mybatis-plus】源码分析_第45张图片

SqlSessionTemplate创建

初始化SqlSessionTemplate
【mybatis和mybatis-plus】源码分析_第46张图片
【mybatis和mybatis-plus】源码分析_第47张图片
【mybatis和mybatis-plus】源码分析_第48张图片
构造方法中创建SqlSession的代理对象
【mybatis和mybatis-plus】源码分析_第49张图片

mybatis-plus

核心类和接口的说明

MybatisPlusProperties

MP的配置注入,如configLocation(mybatis的config文件)、mapperLocations(mapper文件路径)、MybatisConfiguration、globalConfig等

【mybatis和mybatis-plus】源码分析_第50张图片
【mybatis和mybatis-plus】源码分析_第51张图片

MybatisConfiguration

继承自mybatis的Configuration,重写了一些方法
【mybatis和mybatis-plus】源码分析_第52张图片

GlobalConfig

MP的全局配置,如banner、MetaObjectHandler(元对象字段填充控制器)、IdentifierGenerator(主键生成器)等

【mybatis和mybatis-plus】源码分析_第53张图片

MybatisPlusInterceptor

实现了Mybatis的Interceptor接口,用于执行过程的拦截

【mybatis和mybatis-plus】源码分析_第54张图片
在intercept方法中,会解析mybatis的入参invocation,将解析完的参数传入到各个InnerInterceptor扩展的查询/修改方法中
【mybatis和mybatis-plus】源码分析_第55张图片

InnerInterceptor接口

Mp的自定义扩展拦截器
【mybatis和mybatis-plus】源码分析_第56张图片

自定义扩展InnerInterceptor

官方提供了几个拦截器,我们可以在最前面先添加我们自己的拦截器,在执行的时候会最高优先级执行,如果是作为根模块来封装,可以通过责任链模式来构造一个我们自己的拦截器链,业务项目继承封装的接口即可
【mybatis和mybatis-plus】源码分析_第57张图片

RootMybatisInterceptor自定义拦截器链

AbstractMybatisInterceptor

public abstract class AbstractMybatisInterceptor implements InnerInterceptor {

    /**
     * 查询拦截
     * @param executor
     * @param ms
     * @param parameter
     * @param rowBounds
     * @param resultHandler
     * @param boundSql
     * @param chain
     * @return
     * @throws SQLException
     */
    public abstract boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException;

    /**
     * 修改拦截
     * @param executor
     * @param ms
     * @param parameter
     * @param chain
     * @return
     * @throws SQLException
     */
    public abstract boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException;

    public int priority() {
        return 0;
    }
}

RootMybatisInterceptor

public class RootMybatisInterceptor extends AbstractMybatisInterceptor {

    private final AbstractMybatisInterceptor[] interceptors;

    public RootMybatisInterceptor(AbstractMybatisInterceptor[] interceptorChain) {
        this.interceptors = interceptorChain;
    }

    @Override
    public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        InterceptorChain interceptorChain = new InterceptorChain(interceptors);
        return this.queryIntercept(executor, ms, parameter, rowBounds, resultHandler, boundSql, interceptorChain);
    }

    @Override
    public boolean willDoUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException {
        InterceptorChain interceptorChain = new InterceptorChain(interceptors);
        return this.updateIntercept(executor, ms, parameter, interceptorChain);
    }

    @Override
    public boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
        return chain.next(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
    }

    @Override
    public boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
        return chain.next(executor, ms, parameter, chain);
    }

    @Override
    public int priority() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

InterceptorChain

public class InterceptorChain {

    private final List<AbstractMybatisInterceptor> chains;
    private final AtomicInteger index;

    public InterceptorChain(AbstractMybatisInterceptor[] interceptorChains) {
        List<AbstractMybatisInterceptor> chainList = Arrays.stream(interceptorChains).sorted(Comparator.comparingInt(AbstractMybatisInterceptor::priority)).collect(Collectors.toList());
        chains = new ArrayList<>();
        index = new AtomicInteger(0);
        chains.addAll(chainList);
    }

    public boolean next(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
        int curIndex = index.getAndIncrement();
        if (curIndex == chains.size()) {
            return true;
        }
        AbstractMybatisInterceptor abstractMybatisInterceptor = chains.get(curIndex);
        return abstractMybatisInterceptor.queryIntercept(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
    }

    public boolean next(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
        int curIndex = index.getAndIncrement();
        if (curIndex == chains.size()) {
            return true;
        }
        AbstractMybatisInterceptor abstractMybatisInterceptor = chains.get(curIndex);
        return abstractMybatisInterceptor.updateIntercept(executor, ms, parameter, chain);
    }

    public List<AbstractMybatisInterceptor> getChains() {
        return Collections.unmodifiableList(chains);
    }


}

WriteReadMybatisIntercepts读写分离插件

@Slf4j
public class WriteReadMybatisIntercepts extends AbstractMybatisInterceptor {

    @Override
    public boolean queryIntercept(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, InterceptorChain chain) throws SQLException {
        DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobalEnum.READ);
        return chain.next(executor, ms, parameter, rowBounds, resultHandler, boundSql, chain);
    }

    @Override
    public boolean updateIntercept(Executor executor, MappedStatement ms, Object parameter, InterceptorChain chain) throws SQLException {
        DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobalEnum.WRITE);
        return chain.next(executor, ms, parameter, chain);
    }
}

MybatisPlusAutoConfiguration

创建SqlSessionFactory

【mybatis和mybatis-plus】源码分析_第58张图片
创建MybatisSqlSessionFactoryBean,将DataSource、MybatisPlusProperties、MybatisConfiguration、GlobalConfig等参数设置到MybatisSqlSessionFactoryBean中,调用MybatisSqlSessionFactoryBean.getObject()来构建实例

【mybatis和mybatis-plus】源码分析_第59张图片

MybatisSqlSessionFactoryBean

拷贝自Mybatis的SqlSessionFactoryBean,基本内容和SqlSessionFactoryBean一致

基于MybatisSqlSessionFactoryBean,他是一个factoryBean
返回bean类型为SqlSessionFactory
【mybatis和mybatis-plus】源码分析_第60张图片

getObject

先判断sqlSessionFactory是否有值,没有值会进行初始化

【mybatis和mybatis-plus】源码分析_第61张图片
afterPropertiesSet
【mybatis和mybatis-plus】源码分析_第62张图片

buildSqlSessionFactory

【mybatis和mybatis-plus】源码分析_第63张图片

封装Mybatis的Configuration,数据来源自前面传入的成员变量,需要满足mybatis需要的配置数据
【mybatis和mybatis-plus】源码分析_第64张图片
【mybatis和mybatis-plus】源码分析_第65张图片
赋值targetConfiguration
【mybatis和mybatis-plus】源码分析_第66张图片
【mybatis和mybatis-plus】源码分析_第67张图片
初始化Environment,将SpringManagedTransactionFactory作为事务工厂
【mybatis和mybatis-plus】源码分析_第68张图片
mapper文件解析,这段代码和mybatis原生一样
【mybatis和mybatis-plus】源码分析_第69张图片
最终调用到MybatisMapperRegistry.addMapper方法
【mybatis和mybatis-plus】源码分析_第70张图片
该方法与mybatis的MapperRegistry.addMapper区别是替换了一些类,大致是一样的
下图是mybatis的MapperRegistry
【mybatis和mybatis-plus】源码分析_第71张图片

MybatisMapperAnnotationBuilder.parse()

与mybatis源码不同的是,加入了拦截策略的初始化,创建CRUD的mapperStatement加入到缓存中
区别如下两图所示

【mybatis和mybatis-plus】源码分析_第72张图片

【mybatis和mybatis-plus】源码分析_第73张图片

parserInjector()

由于我们自定义的Mapper继承了BaseMapper,在BaseMapper中有默认的增删改查方法

【mybatis和mybatis-plus】源码分析_第74张图片
【mybatis和mybatis-plus】源码分析_第75张图片

【mybatis和mybatis-plus】源码分析_第76张图片
在这里插入图片描述

  1. 获取Mapper上实体注解
  2. 基于实体封装TableInfo(表结构对象,包含表名、id字段、是否新增/修改填充字段等)
    【mybatis和mybatis-plus】源码分析_第77张图片
  3. 返回所有MP内置CRUD方法(基于DefaultSqlInjector,可以参考来实现自己的内置方法)
    【mybatis和mybatis-plus】源码分析_第78张图片
    【mybatis和mybatis-plus】源码分析_第79张图片
  4. 循环注入自定义方法(基于第三步获取的所有方法,依次遍历添加到MappedStatement缓存中)
    【mybatis和mybatis-plus】源码分析_第80张图片
    【mybatis和mybatis-plus】源码分析_第81张图片
    内置方法都需要继承AbstractMethod,基于模板方法调用各自的injectMappedStatement方法
    下图实例Insert

    【mybatis和mybatis-plus】源码分析_第82张图片
    injectMappedStatement最后一步addInsertMappedStatement,由父类AbstractMethod默认实现,最终调用addMappedStatement方法
    【mybatis和mybatis-plus】源码分析_第83张图片
AbstractMethod.addMappedStatement

最终由MapperBuilderAssistant.addMappedStatement添加到mappedStatements中

【mybatis和mybatis-plus】源码分析_第84张图片
insert入参
【mybatis和mybatis-plus】源码分析_第85张图片
添加到mybatis
【mybatis和mybatis-plus】源码分析_第86张图片

XMLMapperBuilder.parse()解析完毕后,获取SqlSessionFactory实现

【mybatis和mybatis-plus】源码分析_第87张图片
mp会在这里初始化一些默认配置(如主键生成器),最后依然是调用myabtis的.build方法来构建并返回SqlSessionFactory的实现
【mybatis和mybatis-plus】源码分析_第88张图片
这里myabtis直接返回了DefaultSqlSessionFactory
下图为mybatis的SqlSessionFactoryBuilder.build
【mybatis和mybatis-plus】源码分析_第89张图片

SqlSessionTemplate的bean创建

MP

@InterceptorIgnore的拦截处理

初始化

MybatisMapperAnnotationBuilder.parse();
与mybatis不同的是,在这里插入执行了InterceptorIgnoreHelper.initSqlParserInfoCache(type)(根据Mapper类上注解来初始化)
在后面插入执行了InterceptorIgnoreHelper.initSqlParserInfoCache(ignoreStrategy, mapperName, method)(根据具体方法上注解来初始化)

mapper类上是否存在@InterceptorIgnore注解
【mybatis和mybatis-plus】源码分析_第90张图片
【mybatis和mybatis-plus】源码分析_第91张图片
方法上是否存在@InterceptorIgnore注解
【mybatis和mybatis-plus】源码分析_第92张图片
【mybatis和mybatis-plus】源码分析_第93张图片

构建IgnoreStrategy

在这里插入图片描述
【mybatis和mybatis-plus】源码分析_第94张图片

缓存IgnoreStrategy

【mybatis和mybatis-plus】源码分析_第95张图片
【mybatis和mybatis-plus】源码分析_第96张图片

使用

你可能感兴趣的:(mybatis)