天天看點

Mybatis執行流程以及整合Spring源碼分析

一,簡述

mybatis的作用就是操作資料庫,其實就是封裝參數,生成sql,執行sql,封裝結果,其實基本就是這幾個大的步驟,mybatis和spring是怎麼整合的呢,以及如何一步一步執行的,具體請看下面原理分析

二,源碼分析

2.1:SqlSessionFactoryBean

具體是在mybatis-spring整合包中,SqlSessionFactoryBean是加載mybatis配置檔案以及生成sqlSession的入口

Mybatis執行流程以及整合Spring源碼分析

2.2:FactoryBean

spring源碼有所了解的應該清楚這個factoryBean是一個生成特殊複雜的bean,以及和beanFactory的區:别,factoryBean中的一個重要的方法就是getObject方法,其實就是根據這個方法傳回一個特殊的自定義的bean對象,而且bean建立的時候,會建立一個&sqlSessionFactoryBean 和sqlSessionFactory對象,就是會建立本身bean攜帶&和getObject傳回的對象,而且getObject是使用的時候才會被調用的,使用的時候才會建立這個bean交給spring來管理

Mybatis執行流程以及整合Spring源碼分析

調用getObejct擷取sqlsessionfactory對象

public SqlSessionFactory FactoryBean() throws Exception {
        if (this.sqlSessionFactory == null) {
            this.afterPropertiesSet();
        }

        return this.sqlSessionFactory;
    }
           
Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

建立一個DefaultSqlSessionFactory,是以在何時建立的,就在這裡

Mybatis執行流程以及整合Spring源碼分析

以上就是和spring結合情況下,建立出來sqlSessionFactory的過程,其實就是建立一個SqlSessionFactoryBean對象,然後調用getObject的方法,就可以做到加載mybatis的配置以及資料的封裝的,以及擷取一個sqlSessionFactory對象,交個spring來管理

2.3:Spring中建立SqlSessionFactory對象

建立一個SqlSessionFactory,具體代碼執行的邏輯如下,其實就是做了一些資料的封裝,并調用getObject的方法,然後交給spring管理

Mybatis執行流程以及整合Spring源碼分析

2.4:spring-boot建立SqlSessionFactory對象

MybatisAutoConfiguration

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析
@Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
      //還是這個對象SqlSessionFactoryBean 來處理的,最後還是getObejct擷取的
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }

        this.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);
        }

        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        Set<String> factoryPropertyNames = (Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
        if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
            factory.setScriptingLanguageDrivers(this.languageDrivers);
            if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
                defaultLanguageDriver = this.languageDrivers[0].getClass();
            }
        }

        if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
            factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
        }

        this.applySqlSessionFactoryBeanCustomizers(factory);
    	//具體還是在這裡做出來的
        return factory.getObject();
    }           

2.5:SqlSessionTemplate建立

MybatisAutoConfiguration中SqlSessionTemplate中建立

Mybatis執行流程以及整合Spring源碼分析

2.6:SqlSessionTemplate建立

建立SqlSessionTemplate 的時候,SqlSessionFactory為上文提到的DefaultSqlSessionFactory,SqlSession為這裡生成的代理對象,繼續根據代理對象是哪一個呢?

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

SqlSessionInterceptor

代理對象為這個類,看一下裡面的invoke方法

invoke

代理調用

getSqlSession

擷取sqlSession

Mybatis執行流程以及整合Spring源碼分析

openSession

擷取Executor

Mybatis執行流程以及整合Spring源碼分析

newExecutor

InterceptorChain鍊執行

Mybatis執行流程以及整合Spring源碼分析

pluginAll

目标的增強

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

小結:

源碼看到了這裡,基本是生成了sqlSessionFactrory,SqlSessionTemplate,以及SqlSession(其實是代理),以及代理對象的invoke的執行,基本就是一些後續執行的前提,是以這些bean的建立基本都應該有所了解了

三,mappper的代理對象生成

在調用的時候,我們隻是寫了一個mapper接口,并未寫實作,但是mapper中的方法和xml中的方法都是對應的,會将mapper全類名接口+方法名作為key存放在map中,每一個都是一個MappedStatement對象,存放在configuration的全局配置中,根據key擷取到MappedStatement對象,根據代理對象執行相應的邏輯

MapperScannerConfigurer

postProcessBeanDefinitionRegistry

ClassPathMapperScanner 進行掃描,this.basePackage是掃描的包,一般就是mapper接口所在的包,springboot中是預設啟動類目前包下的類被掃描 com.clover.**.mapper

Mybatis執行流程以及整合Spring源碼分析

scan

Mybatis執行流程以及整合Spring源碼分析

doScan

執行的含義就是生成bean對象

Mybatis執行流程以及整合Spring源碼分析

processBeanDefinitions

修改beanClass為MapperFactoryBean.class,這樣建立對象的時候,就會調用MapperFactoryBean中的方法

執行afterPropertiesSet方法,然後就是往cinfiguration中添加mapper接口對象

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

MapperFactoryBean

實作了FactoryBean,擷取mapper的時候,調用getobject方法,擷取代理對象

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

getMapper

Mybatis執行流程以及整合Spring源碼分析

newInstance

擷取mapper的代理對象,都是MapperProxy進行的代理,是以到此為止,就可以知道,啟動的時候,已經做到了mapper的對象是從MapperProxy代理對象進行跟蹤進行的

Mybatis執行流程以及整合Spring源碼分析

三,調用執行代理對象mapperProxy

MapperProxy

invoke

每一個mapper調用方法的時候,就會調用invoke執行,然後調用到MapperMethod的excute方法,根據類型執行相應的增删改查

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

MapperMethod

execute

判斷類型,其實這個類型就是mapperXml定義的标簽以及id和與之對應的方法名一一對應的,到了這裡,可能就比較熟悉了,因為對于sql的增删改查相比都是比較的熟悉,這裡其實就是下面可以猜測到的,拼裝sql,和參數,然後執行sql,傳回結果,處理傳回結果

Mybatis執行流程以及整合Spring源碼分析

比如一個executeForMany為例

SqlSessionTemplate調用selectList,然後使用代理對象調用

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

此時的sqlsesion就是sqlSessionTemplate

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

代理對象為SqlSessionInterceptor,此時調用就是invoke方法

Mybatis執行流程以及整合Spring源碼分析

invoke方法調用

*首先擷取sqlSession,然後 裡面大緻的流程就是建立一個DefaultSqlSession對象,并建立一個Executor的對象或者代理對象,比如pageHelper就是代理了此對象Executor

*擷取Executor對象,并檢查是否是需要代理

*執行代理方法

*處理一下事務的邏輯

Mybatis執行流程以及整合Spring源碼分析
Mybatis執行流程以及整合Spring源碼分析

四,mybatis的執行流程總結

執行流程圖

Mybatis執行流程以及整合Spring源碼分析

mapperMethod執行:主要是請求參數的解析

excutor:主要是指StatementHandler的建立,包含 BoundSql 的建立、ParameterHandler和 ResultSetHandler 的建立。

statementHandler 執行:主要執行sql 并對結果集進行處理

參數組裝是在excutor建立之前,攔截器是在建立excutor的時候,是以前期就是參數組裝,BoundSql建立 是在StatementHandler建立的時候,在ParameterHandler和 ResultSetHandler 之前。