1. MyBatis的動(dòng)態(tài)代理實(shí)現(xiàn)細(xì)節(jié)

        共 15603字,需瀏覽 32分鐘

         ·

        2021-01-19 00:52


        點(diǎn)擊上方?藍(lán)字?關(guān)注我們!



        Java,Python,C/C++,Linux,PHP,Go,C#,QT,大數(shù)據(jù),算法,軟件教程,前端,簡(jiǎn)歷,畢業(yè)設(shè)計(jì)等分類(lèi),資源在不斷更新中... 點(diǎn)擊領(lǐng)取!

        前言

        一直以來(lái)都在使用MyBatis做持久化框架,也知道當(dāng)我們定義XXXMapper接口類(lèi)并利用它來(lái)做CRUD操作時(shí),Mybatis是利用了動(dòng)態(tài)代理的技術(shù)幫我們生成代理類(lèi)。那么動(dòng)態(tài)代理內(nèi)部的實(shí)現(xiàn)細(xì)節(jié)到底是怎么的呀?XXXMapper.java類(lèi)和XXXMapper.xml到底是如何關(guān)聯(lián)起來(lái)的呀?本篇文章就來(lái)詳細(xì)剖析下MyBatis的動(dòng)態(tài)代理的具體實(shí)現(xiàn)機(jī)制。

        MyBatis的核心組件及應(yīng)用

        在詳細(xì)探究MyBatis中動(dòng)態(tài)代理機(jī)制之前,先來(lái)補(bǔ)充一下基礎(chǔ)知識(shí),認(rèn)識(shí)一下MyBatis的核心組件。

        • SqlSessionFactoryBuilder(構(gòu)造器):它可以從XML、注解或者手動(dòng)配置Java代碼來(lái)創(chuàng)建SqlSessionFactory。
        • SqlSessionFactory: 用于創(chuàng)建SqlSession (會(huì)話(huà)) 的工廠(chǎng)
        • SqlSession: SqlSession是Mybatis最核心的類(lèi),可以用于執(zhí)行語(yǔ)句、提交或回滾事務(wù)以及獲取映射器Mapper的接口
        • SQL Mapper:它是由一個(gè)Java接口和XML文件(或注解)構(gòu)成的,需要給出對(duì)應(yīng)的SQL和映射規(guī)則,它負(fù)責(zé)發(fā)送SQL去執(zhí)行,并返回結(jié)果

        注意:現(xiàn)在我們使用Mybatis,一般都是和Spring框架整合在一起使用,這種情況下,SqlSession將被Spring框架所創(chuàng)建,所以往往不需要我們使用SqlSessionFactoryBuilder或者SqlSessionFactory去創(chuàng)建SqlSession

        下面展示一下如何使用MyBatis的這些組件,或者如何快速使用MyBatis:

        1. 數(shù)據(jù)庫(kù)表
        ???CREATE?TABLE??user(
        ?????id?int,
        ?????name?VARCHAR(255)?not?NULL?,
        ?????age?int?,
        ?????PRIMARY?KEY?(id)
        ???)ENGINE?=INNODB?DEFAULT?CHARSET=utf8;
        1. 聲明一個(gè)User類(lèi)
        ???@Data
        ???public?class?User?{
        ???????private?int?id;
        ???????private?int?age;
        ???????private?String?name;
        ???
        ???????@Override
        ???????public?String?toString()?{
        ???????????return?"User{"?+
        ???????????????????"id="?+?id?+
        ???????????????????",?age="?+?age?+
        ???????????????????",?name='"?+?name?+?'\''?+
        ???????????????????'
        }';
        ???????}
        ???}
        1. 定義一個(gè)全局配置文件mybatis-config.xml (關(guān)于配置文件中具體屬性標(biāo)簽解釋參閱官方文檔)
        ???"1.0"?encoding="UTF-8"??>
        ???"-//mybatis.org//DTD?Config?3.0//EN"
        ???????????"http://mybatis.org/dtd/mybatis-3-config.dtd">
        ???
        ???
        ???????
        ???????"development">
        ???????????"development">
        ???????????????
        ???????????????type="MANAGED"?/>
        ???????????????
        ???????????????type="POOLED">
        ???????????????????"driver"?value="com.mysql.jdbc.Driver"?/>
        ???????????????????"url"?value="jdbc:mysql://localhost:3306/test"/>
        ???????????????????"username"?value="root"?/>
        ???????????????????"password"?value="root"?/>
        ???????????????
        ???????????
        ???????
        ???????
        ???????
        ???????????"mapper/UserMapper.xml"/>
        ???????

        ???

        1. UserMapper接口
        ???public?interface?UserMapper?{
        ???????User?selectById(int?id);
        ???}
        1. UserMapper文件
        ???"-//mybatis.org//DTD?Mapper?3.0//EN"
        ???????????"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        ???
        ???"com.pjmike.mybatis.UserMapper">
        ???????"selectById"?parameterType="int"
        ???????????????resultType="com.pjmike.mybatis.User">
        ????????????????SELECT?id,name,age?FROM?user?where?id=?#{id}
        ??????????
        ???
        1. 測(cè)試類(lèi)
        ???public?class?MybatisTest?{
        ???????private?static?SqlSessionFactory?sqlSessionFactory;
        ???????static?{
        ???????????try?{
        ???????????????sqlSessionFactory?=?new?SqlSessionFactoryBuilder()
        ???????????????????????.build(Resources.getResourceAsStream("mybatis-config.xml"));
        ???????????}?catch?(IOException?e)?{
        ???????????????e.printStackTrace();
        ???????????}
        ???????}
        ???????public?static?void?main(String[]?args)?{
        ???????????try?(SqlSession?sqlSession?=?sqlSessionFactory.openSession())?{
        ???????????????UserMapper?userMapper?=?sqlSession.getMapper(UserMapper.class);
        ???????????????User?user?=?userMapper.selectById(1);
        ???????????????System.out.println("User?:?"?+?user);
        ???????????}
        ???????}
        ???}
        ???//?結(jié)果:
        ???User?:?User{id=1,?age=21,?name='pjmike'}

        上面的例子簡(jiǎn)單的展示了如何使用MyBatis,與此同時(shí),我也將用這個(gè)例子來(lái)進(jìn)一步探究MyBatis動(dòng)態(tài)原理的實(shí)現(xiàn)。

        MyBatis動(dòng)態(tài)代理的實(shí)現(xiàn)

        public?static?void?main(String[]?args)?{
        ????try?(SqlSession?sqlSession?=?sqlSessionFactory.openSession())?{
        ????????UserMapper?userMapper?=?sqlSession.getMapper(UserMapper.class);//?<1>
        ????????User?user?=?userMapper.selectById(1);
        ????????System.out.println("User?:?"?+?user);
        ????}
        }

        在前面的例子中,我們使用sqlSession.getMapper()方法獲取UserMapper對(duì)象,實(shí)際上這里我們是獲取了UserMapper接口的代理類(lèi),然后再由代理類(lèi)執(zhí)行方法。那么這個(gè)代理類(lèi)是如何生成的呢?在探究動(dòng)態(tài)代理類(lèi)如何生成之前,我們先來(lái)看下SqlSessionFactory工廠(chǎng)的創(chuàng)建過(guò)程做了哪些準(zhǔn)備工作,比如說(shuō)mybatis-config配置文件是如何讀取的,映射器文件是如何讀取的?

        mybatis全局配置文件解析

        private?static?SqlSessionFactory?sqlSessionFactory;
        static?{
        ????try?{
        ????????sqlSessionFactory?=?new?SqlSessionFactoryBuilder()
        ????????????????.build(Resources.getResourceAsStream("mybatis-config.xml"));
        ????}?catch?(IOException?e)?{
        ????????e.printStackTrace();
        ????}
        }

        我們使用new SqlSessionFactoryBuilder().build()的方式創(chuàng)建SqlSessionFactory工廠(chǎng),走進(jìn)build方法

        public?SqlSessionFactory?build(InputStream?inputStream,?Properties?properties)?{
        ???return?build(inputStream,?null,?properties);
        ?}

        ?public?SqlSessionFactory?build(InputStream?inputStream,?String?environment,?Properties?properties)?{
        ???try?{
        ?????XMLConfigBuilder?parser?=?new?XMLConfigBuilder(inputStream,?environment,?properties);
        ?????return?build(parser.parse());
        ???}?catch?(Exception?e)?{
        ?????throw?ExceptionFactory.wrapException("Error?building?SqlSession.",?e);
        ???}?finally?{
        ?????ErrorContext.instance().reset();
        ?????try?{
        ???????inputStream.close();
        ?????}?catch?(IOException?e)?{
        ???????//?Intentionally?ignore.?Prefer?previous?error.
        ?????}
        ???}
        ?}

        對(duì)于mybatis的全局配置文件的解析,相關(guān)解析代碼位于XMLConfigBuilder的parse()方法中:

        public?Configuration?parse()?{
        ???if?(parsed)?{
        ?????throw?new?BuilderException("Each?XMLConfigBuilder?can?only?be?used?once.");
        ???}
        ???parsed?=?true;
        ???//解析全局配置文件
        ???parseConfiguration(parser.evalNode("/configuration"));
        ???return?configuration;
        ?}

        ?private?void?parseConfiguration(XNode?root)?{
        ???try?{
        ?????//issue?#117?read?properties?first
        ?????propertiesElement(root.evalNode("properties"));
        ?????Properties?settings?=?settingsAsProperties(root.evalNode("settings"));
        ?????loadCustomVfs(settings);
        ?????loadCustomLogImpl(settings);
        ?????typeAliasesElement(root.evalNode("typeAliases"));
        ?????pluginElement(root.evalNode("plugins"));
        ?????objectFactoryElement(root.evalNode("objectFactory"));
        ?????objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        ?????reflectorFactoryElement(root.evalNode("reflectorFactory"));
        ?????settingsElement(settings);
        ?????//?read?it?after?objectFactory?and?objectWrapperFactory?issue?#631
        ?????environmentsElement(root.evalNode("environments"));
        ?????databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        ?????typeHandlerElement(root.evalNode("typeHandlers"));
        ?????//解析mapper映射器文件
        ?????mapperElement(root.evalNode("mappers"));
        ???}?catch?(Exception?e)?{
        ?????throw?new?BuilderException("Error?parsing?SQL?Mapper?Configuration.?Cause:?"?+?e,?e);
        ???}
        ?}

        從parseConfiguration方法的源代碼中很容易就可以看出它對(duì)mybatis全局配置文件中各個(gè)元素屬性的解析。當(dāng)然最終解析后返回一個(gè)Configuration對(duì)象,Configuration是一個(gè)很重要的類(lèi),它包含了Mybatis的所有配置信息,它是通過(guò)XMLConfigBuilder取錢(qián)構(gòu)建的,Mybatis通過(guò)XMLConfigBuilder讀取mybatis-config.xml中配置的信息,然后將這些信息保存到Configuration中

        映射器Mapper文件的解析

        //解析mapper映射器文件
        mapperElement(root.evalNode("mappers"));

        該方法是對(duì)全局配置文件中mappers屬性的解析,走進(jìn)去:

        [](https://pjmike-1253796536.cos.ap-beijing.myqcloud.com/mybatis/mapper xml解析.png)mapper xml

        mapperParser.parse()方法就是XMLMapperBuilder對(duì)Mapper映射器文件進(jìn)行解析,可與XMLConfigBuilder進(jìn)行類(lèi)比

        public?void?parse()?{
        ??if?(!configuration.isResourceLoaded(resource))?{
        ????configurationElement(parser.evalNode("/mapper"));?//解析映射文件的根節(jié)點(diǎn)mapper元素
        ????configuration.addLoadedResource(resource);??
        ????bindMapperForNamespace();?//重點(diǎn)方法,這個(gè)方法內(nèi)部會(huì)根據(jù)namespace屬性值,生成動(dòng)態(tài)代理類(lèi)
        ??}
        ??parsePendingResultMaps();
        ??parsePendingCacheRefs();
        ??parsePendingStatements();
        }
        • configurationElement(XNode context)方法

        該方法主要用于將mapper文件中的元素信息,比如insertselect這等信息解析到MappedStatement對(duì)象,并保存到Configuration類(lèi)中的mappedStatements屬性中,以便于后續(xù)動(dòng)態(tài)代理類(lèi)執(zhí)行CRUD操作時(shí)能夠獲取真正的Sql語(yǔ)句信息

        configurationElement

        buildStatementFromContext方法就用于解析insert、select這類(lèi)元素信息,并將其封裝成MappedStatement對(duì)象,具體的實(shí)現(xiàn)細(xì)節(jié)這里就不細(xì)說(shuō)了。

        • bindMapperForNamespace()方法

        該方法是核心方法,它會(huì)根據(jù)mapper文件中的namespace屬性值,為接口生成動(dòng)態(tài)代理類(lèi),這就來(lái)到了我們的主題內(nèi)容——?jiǎng)討B(tài)代理類(lèi)是如何生成的。

        動(dòng)態(tài)代理類(lèi)的生成

        bindMapperForNamespace方法源碼如下所示:

        private?void?bindMapperForNamespace()?{
        ???//獲取mapper元素的namespace屬性值
        ???String?namespace?=?builderAssistant.getCurrentNamespace();
        ???if?(namespace?!=?null)?{
        ?????Class?boundType?=?null;
        ?????try?{
        ???????//?獲取namespace屬性值對(duì)應(yīng)的Class對(duì)象
        ???????boundType?=?Resources.classForName(namespace);
        ?????}?catch?(ClassNotFoundException?e)?{
        ???????//如果沒(méi)有這個(gè)類(lèi),則直接忽略,這是因?yàn)閚amespace屬性值只需要唯一即可,并不一定對(duì)應(yīng)一個(gè)XXXMapper接口
        ???????//沒(méi)有XXXMapper接口的時(shí)候,我們可以直接使用SqlSession來(lái)進(jìn)行增刪改查
        ?????}
        ?????if?(boundType?!=?null)?{
        ???????if?(!configuration.hasMapper(boundType))?{
        ?????????//?Spring?may?not?know?the?real?resource?name?so?we?set?a?flag
        ?????????//?to?prevent?loading?again?this?resource?from?the?mapper?interface
        ?????????//?look?at?MapperAnnotationBuilder#loadXmlResource
        ?????????configuration.addLoadedResource("namespace:"?+?namespace);
        ?????????//如果namespace屬性值有對(duì)應(yīng)的Java類(lèi),調(diào)用Configuration的addMapper方法,將其添加到MapperRegistry中
        ?????????configuration.addMapper(boundType);
        ???????}
        ?????}
        ???}
        ?}

        這里提到了Configuration的addMapper方法,實(shí)際上Configuration類(lèi)里面通過(guò)MapperRegistry對(duì)象維護(hù)了所有要生成動(dòng)態(tài)代理類(lèi)的XxxMapper接口信息,可見(jiàn)Configuration類(lèi)確實(shí)是相當(dāng)重要一類(lèi)

        public?class?Configuration?{
        ????...
        ????protected?MapperRegistry?mapperRegistry?=?new?MapperRegistry(this);
        ????...
        ????public??void?addMapper(Class?type)?{
        ??????mapperRegistry.addMapper(type);
        ????}
        ????public??T?getMapper(Class?type,?SqlSession?sqlSession)?{
        ??????return?mapperRegistry.getMapper(type,?sqlSession);
        ????}
        ????...
        }

        其中兩個(gè)重要的方法:getMapper()和addMapper()

        • getMapper(): 用于創(chuàng)建接口的動(dòng)態(tài)類(lèi)
        • addMapper(): mybatis在解析配置文件時(shí),會(huì)將需要生成動(dòng)態(tài)代理類(lèi)的接口注冊(cè)到其中

        1. Configuration#addMappper()

        Configuration將addMapper方法委托給MapperRegistry的addMapper進(jìn)行的,源碼如下:

        public??void?addMapper(Class?type)?{
        ??//?這個(gè)class必須是一個(gè)接口,因?yàn)槭鞘褂肑DK動(dòng)態(tài)代理,所以需要是接口,否則不會(huì)針對(duì)其生成動(dòng)態(tài)代理
        ??if?(type.isInterface())?{
        ????if?(hasMapper(type))?{
        ??????throw?new?BindingException("Type?"?+?type?+?"?is?already?known?to?the?MapperRegistry.");
        ????}
        ????boolean?loadCompleted?=?false;
        ????try?{
        ??????//?生成一個(gè)MapperProxyFactory,用于之后生成動(dòng)態(tài)代理類(lèi)
        ??????knownMappers.put(type,?new?MapperProxyFactory<>(type));
        ??????//以下代碼片段用于解析我們定義的XxxMapper接口里面使用的注解,這主要是處理不使用xml映射文件的情況
        ??????MapperAnnotationBuilder?parser?=?new?MapperAnnotationBuilder(config,?type);
        ??????parser.parse();
        ??????loadCompleted?=?true;
        ????}?finally?{
        ??????if?(!loadCompleted)?{
        ????????knownMappers.remove(type);
        ??????}
        ????}
        ??}
        }

        MapperRegistry內(nèi)部維護(hù)一個(gè)映射關(guān)系,每個(gè)接口對(duì)應(yīng)一個(gè)MapperProxyFactory(生成動(dòng)態(tài)代理工廠(chǎng)類(lèi))

        private?final?Map,?MapperProxyFactory>?knownMappers?=?new?HashMap<>();

        這樣便于在后面調(diào)用MapperRegistry的getMapper()時(shí),直接從Map中獲取某個(gè)接口對(duì)應(yīng)的動(dòng)態(tài)代理工廠(chǎng)類(lèi),然后再利用工廠(chǎng)類(lèi)針對(duì)其接口生成真正的動(dòng)態(tài)代理類(lèi)。

        2. Configuration#getMapper()

        Configuration的getMapper()方法內(nèi)部就是調(diào)用MapperRegistry的getMapper()方法,源代碼如下:

        public??T?getMapper(Class?type,?SqlSession?sqlSession)?{
        ??//根據(jù)Class對(duì)象獲取創(chuàng)建動(dòng)態(tài)代理的工廠(chǎng)對(duì)象MapperProxyFactory
        ??final?MapperProxyFactory?mapperProxyFactory?=?(MapperProxyFactory)?knownMappers.get(type);
        ??if?(mapperProxyFactory?==?null)?{
        ????throw?new?BindingException("Type?"?+?type?+?"?is?not?known?to?the?MapperRegistry.");
        ??}
        ??try?{
        ????//這里可以看到每次調(diào)用都會(huì)創(chuàng)建一個(gè)新的代理對(duì)象返回
        ????return?mapperProxyFactory.newInstance(sqlSession);
        ??}?catch?(Exception?e)?{
        ????throw?new?BindingException("Error?getting?mapper?instance.?Cause:?"?+?e,?e);
        ??}
        }

        從上面可以看出,創(chuàng)建動(dòng)態(tài)代理類(lèi)的核心代碼就是在MapperProxyFactory.newInstance方法中,源碼如下:

        protected?T?newInstance(MapperProxy?mapperProxy)?{
        ??//這里使用JDK動(dòng)態(tài)代理,通過(guò)Proxy.newProxyInstance生成動(dòng)態(tài)代理類(lèi)
        ??// newProxyInstance的參數(shù):類(lèi)加載器、接口類(lèi)、InvocationHandler接口實(shí)現(xiàn)類(lèi)
        ??//?動(dòng)態(tài)代理可以將所有接口的調(diào)用重定向到調(diào)用處理器InvocationHandler,調(diào)用它的invoke方法
        ??return?(T)?Proxy.newProxyInstance(mapperInterface.getClassLoader(),?new?Class[]?{?mapperInterface?},?mapperProxy);
        }

        public?T?newInstance(SqlSession?sqlSession)?{
        ??final?MapperProxy?mapperProxy?=?new?MapperProxy<>(sqlSession,?mapperInterface,?methodCache);
        ??return?newInstance(mapperProxy);
        }

        PS: 關(guān)于JDK動(dòng)態(tài)代理的詳細(xì)介紹這里就不再細(xì)說(shuō)了,有興趣的可以參閱我之前寫(xiě)的文章:動(dòng)態(tài)代理的原理及其應(yīng)用

        這里的InvocationHandler接口的實(shí)現(xiàn)類(lèi)是MapperProxy,其源碼如下:

        public?class?MapperProxy?implements?InvocationHandler,?Serializable?{

        ??private?static?final?long?serialVersionUID?=?-6424540398559729838L;
        ??private?final?SqlSession?sqlSession;
        ??private?final?Class?mapperInterface;
        ??private?final?Map?methodCache;

        ??public?MapperProxy(SqlSession?sqlSession,?Class?mapperInterface,?Map?methodCache)?{
        ????this.sqlSession?=?sqlSession;
        ????this.mapperInterface?=?mapperInterface;
        ????this.methodCache?=?methodCache;
        ??}

        ??@Override
        ??public?Object?invoke(Object?proxy,?Method?method,?Object[]?args)?throws?Throwable?{
        ????
        ????try?{
        ??????//如果調(diào)用的是Object類(lèi)中定義的方法,直接通過(guò)反射調(diào)用即可
        ??????if?(Object.class.equals(method.getDeclaringClass()))?{
        ????????return?method.invoke(this,?args);
        ??????}?else?if?(isDefaultMethod(method))?{
        ????????return?invokeDefaultMethod(proxy,?method,?args);
        ??????}
        ????}?catch?(Throwable?t)?{
        ??????throw?ExceptionUtil.unwrapThrowable(t);
        ????}
        ????//調(diào)用XxxMapper接口自定義的方法,進(jìn)行代理
        ????//首先將當(dāng)前被調(diào)用的方法Method構(gòu)造成一個(gè)MapperMethod對(duì)象,然后掉用其execute方法真正的開(kāi)始執(zhí)行。
        ????final?MapperMethod?mapperMethod?=?cachedMapperMethod(method);
        ????return?mapperMethod.execute(sqlSession,?args);
        ??}
        ??private?MapperMethod?cachedMapperMethod(Method?method)?{
        ????return?methodCache.computeIfAbsent(method,?k?->?new?MapperMethod(mapperInterface,?method,?sqlSession.getConfiguration()));
        ??}
        ??...
        }

        最終的執(zhí)行邏輯在于MapperMethod類(lèi)的execute方法,源碼如下:

        public?class?MapperMethod?{

        ??private?final?SqlCommand?command;
        ??private?final?MethodSignature?method;

        ??public?MapperMethod(Class?mapperInterface,?Method?method,?Configuration?config)?{
        ????this.command?=?new?SqlCommand(config,?mapperInterface,?method);
        ????this.method?=?new?MethodSignature(config,?mapperInterface,?method);
        ??}
        ??public?Object?execute(SqlSession?sqlSession,?Object[]?args)?{
        ????Object?result;
        ????switch?(command.getType())?{
        ??????//insert語(yǔ)句的處理邏輯
        ??????case?INSERT:?{
        ????????Object?param?=?method.convertArgsToSqlCommandParam(args);
        ????????result?=?rowCountResult(sqlSession.insert(command.getName(),?param));
        ????????break;
        ??????}
        ??????//update語(yǔ)句的處理邏輯
        ??????case?UPDATE:?{
        ????????Object?param?=?method.convertArgsToSqlCommandParam(args);
        ????????result?=?rowCountResult(sqlSession.update(command.getName(),?param));
        ????????break;
        ??????}
        ??????//delete語(yǔ)句的處理邏輯
        ??????case?DELETE:?{
        ????????Object?param?=?method.convertArgsToSqlCommandParam(args);
        ????????result?=?rowCountResult(sqlSession.delete(command.getName(),?param));
        ????????break;
        ??????}
        ??????//select語(yǔ)句的處理邏輯
        ??????case?SELECT:
        ????????if?(method.returnsVoid()?&&?method.hasResultHandler())?{
        ??????????executeWithResultHandler(sqlSession,?args);
        ??????????result?=?null;
        ????????}?else?if?(method.returnsMany())?{
        ??????????result?=?executeForMany(sqlSession,?args);
        ????????}?else?if?(method.returnsMap())?{
        ??????????result?=?executeForMap(sqlSession,?args);
        ????????}?else?if?(method.returnsCursor())?{
        ??????????result?=?executeForCursor(sqlSession,?args);
        ????????}?else?{
        ??????????Object?param?=?method.convertArgsToSqlCommandParam(args);
        ??????????//調(diào)用sqlSession的selectOne方法
        ??????????result?=?sqlSession.selectOne(command.getName(),?param);
        ??????????if?(method.returnsOptional()
        ??????????????&&?(result?==?null?||?!method.getReturnType().equals(result.getClass())))?{
        ????????????result?=?Optional.ofNullable(result);
        ??????????}
        ????????}
        ????????break;
        ??????case?FLUSH:
        ????????result?=?sqlSession.flushStatements();
        ????????break;
        ??????default:
        ????????throw?new?BindingException("Unknown?execution?method?for:?"?+?command.getName());
        ????}
        ????if?(result?==?null?&&?method.getReturnType().isPrimitive()?&&?!method.returnsVoid())?{
        ??????throw?new?BindingException("Mapper?method?'"?+?command.getName()
        ??????????+?"?attempted?to?return?null?from?a?method?with?a?primitive?return?type?("?+?method.getReturnType()?+?").");
        ????}
        ????return?result;
        ??}
        ??...
        }

        在MapperMethod中還有兩個(gè)內(nèi)部類(lèi),SqlCommand和MethodSignature類(lèi),在execute方法中首先用switch case語(yǔ)句根據(jù)SqlCommand的getType()方法,判斷要執(zhí)行的sql類(lèi)型,比如INSET、UPDATE、DELETE、SELECT和FLUSH,然后分別調(diào)用SqlSession的增刪改查等方法。

        慢著,說(shuō)了這么多,那么這個(gè)getMapper()方法什么時(shí)候被調(diào)用呀?實(shí)際是一開(kāi)始我們調(diào)用SqlSession的getMapper()方法:

        UserMapper?userMapper?=?sqlSession.getMapper(UserMapper.class);

        public?class?DefaultSqlSession?implements?SqlSession?{

        ??private?final?Configuration?configuration;
        ??private?final?Executor?executor;
        ??@Override
        ??public??T?getMapper(Class?type)?{
        ????return?configuration.getMapper(type,?this);
        ??}
        ??...
        }

        所以getMapper方法的大致調(diào)用邏輯鏈?zhǔn)牵篠qlSession#getMapper() ——> Configuration#getMapper() ——> MapperRegistry#getMapper() ——> MapperProxyFactory#newInstance() ——> Proxy#newProxyInstance()

        還有一點(diǎn)我們需要注意:我們通過(guò)SqlSession的getMapper方法獲得接口代理來(lái)進(jìn)行CRUD操作,其底層還是依靠的是SqlSession的使用方法。

        小結(jié)

        根據(jù)上面的探究過(guò)程,簡(jiǎn)單畫(huà)了一個(gè)邏輯圖(不一定準(zhǔn)確):

        本篇文章主要介紹了MyBatis的動(dòng)態(tài)原理,回過(guò)頭來(lái),我們需要知道我們使用UserMapper的動(dòng)態(tài)代理類(lèi)進(jìn)行CRUD操作,本質(zhì)上還是通過(guò)SqlSession這個(gè)關(guān)鍵類(lèi)執(zhí)行增刪改查操作,但是對(duì)于SqlSession如何具體執(zhí)行CRUD的操作并沒(méi)有仔細(xì)闡述,有興趣的同學(xué)可以查閱相關(guān)資料。

        參考資料 & 鳴謝

        • http://www.tianshouzhi.com/api/tutorials/mybatis/360
        • http://www.mybatis.org/mybatis-3/



        往期推薦

        用 Arthas 定位 Spring Boot 接口的超時(shí)問(wèn)題,讓?xiě)?yīng)用起飛~

        面試經(jīng)歷:如何從 100 億 URL 中找出相同的 URL?

        SpringBoot 中用注解的方式實(shí)現(xiàn) Redis 分布式鎖

        GET 和 POST的本質(zhì)區(qū)別是什么?原來(lái)我一直理解錯(cuò)了



        END



        若覺(jué)得文章對(duì)你有幫助,隨手轉(zhuǎn)發(fā)分享,也是我們繼續(xù)更新的動(dòng)力。


        長(zhǎng)按二維碼,掃掃關(guān)注哦

        ?「C語(yǔ)言中文網(wǎng)」官方公眾號(hào),關(guān)注手機(jī)閱讀教程??


        必備編程學(xué)習(xí)資料


        目前收集的資料包括:?Java,Python,C/C++,Linux,PHP,go,C#,QT,git/svn,人工智能,大數(shù)據(jù),單片機(jī),算法,小程序,易語(yǔ)言,安卓,ios,PPT,軟件教程,前端,軟件測(cè)試,簡(jiǎn)歷,畢業(yè)設(shè)計(jì),公開(kāi)課?等分類(lèi),資源在不斷更新中...


        點(diǎn)擊“閱讀原文”,立即免費(fèi)領(lǐng)取最新資料!
        ??????
        瀏覽 42
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. 天天操夜夜操人人操 | 99精品全部 | 天天操天天射天天插 | 激情丁香五月天久久久 | 国模私拍视频 |