天天看點

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

抛出疑問

Mybatis-plus的确能讓我們寫少很多重複代碼,非常好用。那麼其中最友善的就是Mapper接口繼承

BaseMapper

就能獲得增删改查的這個功能。那麼這個功能的底層代碼,究竟是怎麼實作的呢?

原生Mybatis配置的原理

畢竟Mybatis-plus是Mybatis的加強,是以Mybatis-plus肯定是基于Mybatis原來的機制來擴充的,沿着這個思路,我們先搞清楚一個問題,就是

原生的mapper.xml檔案最後是怎麼跟對應的Mapper接口産生聯系的

既然是配置,那麼在Mybatis裡肯定有對應的配置類,這個類就是

MappedStatement

。最終在

Configuration

類中把

MappedStatement

對象添加進

mappedStatements

集合中進行管理。源碼如下:

public class Configuration {
    protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");  

    public void addMappedStatement(MappedStatement ms) {
        mappedStatements.put(ms.getId(), ms);
    }
}           

假如有個mapper.xml檔案定義如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yehongzhi.mydemo.mapper.UserMapper">
    <select id="getUserList" resultType="com.yehongzhi.mydemo.model.User">
        select * from user
    </select>
</mapper>           

我們用debug模式來驗證一下:

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

由此可以看出,Mybatis底層在解析Mapper.xml檔案最後是轉成一個

MappedStatement

對象進行管理。跟着這個思路,我們能不能根據特定的規律建立

MappedStatement

對象放進

mappedStatements

集合中,那不就能實作Mybatis-plus的

BaseMapper

的功能了嗎!

Mybatis-plus源碼分析

首先找到

MybatisPlusAutoConfiguration

配置類,會建立MybatisSqlSessionFactoryBean。

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

并設定

MybatisConfiguration

作為配置類。

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

這個

MybatisConfiguration

是很重要的類,裡面會初始化一個

mybatisMapperRegistry

,後面有用。

public class MybatisConfiguration extends Configuration {
    /**
    * Mapper 注冊
    */
    protected final MybatisMapperRegistry mybatisMapperRegistry = new MybatisMapperRegistry(this);
}           

當建立

MybatisSqlSessionFactoryBean

時,會調用

afterPropertiesSet()

方法建立

sqlSessionFactory

@Override
public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
          "Property 'configuration' and 'configLocation' can not specified with together");
    this.sqlSessionFactory = buildSqlSessionFactory();
}           

然後

buildSqlSessionFactory()

方法的主要内容是解析mapper的xml檔案。

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結
Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

然後繼續深入,看

bindMapperForNamespace()

方法。

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

接着用

MapperAnnotationBuilder

類進行解析。

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

接着在parse()方法裡進行基本的SQL注入:

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

關鍵就在這個SQL注入器裡。

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

是以關鍵在于

AbstractMethod

,這裡用了模闆模式。

public abstract class AbstractMethod implements Constants {

    protected Configuration configuration;
    protected LanguageDriver languageDriver;
    protected MapperBuilderAssistant builderAssistant;

    /**
     * 注入自定義方法
     */
    public void inject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
        this.configuration = builderAssistant.getConfiguration();
        this.builderAssistant = builderAssistant;
        this.languageDriver = configuration.getDefaultScriptingLanguageInstance();
        /* 注入自定義方法 */
        injectMappedStatement(mapperClass, modelClass, tableInfo);
    }

    /**
     * 注入自定義 MappedStatement
     *
     * @param mapperClass mapper 接口
     * @param modelClass  mapper 泛型
     * @param tableInfo   資料庫表反射資訊
     * @return MappedStatement
     */
    public abstract MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);
}           

利用模闆模式,子類隻需要重寫

injectMappedStatement()

方法,上面初始化的部分都可以共用。

AbstractMethod

的子類有很多,我們選個有代表性的看一下,就可以推斷其他的用途,比如Insert類。

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

由此可看出,BaseMapper裡的語句資訊模闆,來自于枚舉

SqlMethod

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

最終就是轉成

MappedStatement

對象,然後添加注冊,于是乎就有了這些CRUD操作的方法。

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結

總結

總結一下加載BaseMapper的過程:

  1. 初始化

    MybatisConfiguration

    mybatisMapperRegistry

  2. 解析Mapper類,擷取AbstractMethod集合。
  3. 周遊AbstractMethod集合,然後調用各自實作的

    injectMappedStatement()

    方法,注入SQL。
  4. 添加注冊

    MappedStatement

    對象。

非常感謝你的閱讀,希望這篇文章能給到你幫助和啟發。

覺得有用就點個贊吧,你的點贊是我創作的最大動力~

我是一個努力讓大家記住的程式員。我們下期再見!!!

Mybatis-Plus,BaseMapper源碼分析抛出疑問原生Mybatis配置的原理Mybatis-plus源碼分析總結
能力有限,如果有什麼錯誤或者不當之處,請大家批評指正,一起學習交流!