<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 15318字,需瀏覽 31分鐘

           ·

          2021-01-11 17:44


          點(diǎn)擊上方?泥瓦匠 關(guān)注我!

          前言

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

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

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

          • SqlSessionFactoryBuilder(構(gòu)造器):它可以從XML、注解或者手動(dòng)配置Java代碼來創(chuàng)建SqlSessionFactory。
          • SqlSessionFactory: 用于創(chuàng)建SqlSession (會(huì)話) 的工廠
          • SqlSession: SqlSession是Mybatis最核心的類,可以用于執(zhí)行語句、提交或回滾事務(wù)以及獲取映射器Mapper的接口
          • SQL Mapper:它是由一個(gè)Java接口和XML文件(或注解)構(gòu)成的,需要給出對應(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ù)庫表
          ???CREATE?TABLE??user(
          ?????id?int,
          ?????name?VARCHAR(255)?not?NULL?,
          ?????age?int?,
          ?????PRIMARY?KEY?(id)
          ???)ENGINE?=INNODB?DEFAULT?CHARSET=utf8;
          1. 聲明一個(gè)User類
          ???@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. 測試類
          ???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'}

          上面的例子簡單的展示了如何使用MyBatis,與此同時(shí),我也將用這個(gè)例子來進(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對象,實(shí)際上這里我們是獲取了UserMapper接口的代理類,然后再由代理類執(zhí)行方法。那么這個(gè)代理類是如何生成的呢?在探究動(dòng)態(tài)代理類如何生成之前,我們先來看下SqlSessionFactory工廠的創(chuàng)建過程做了哪些準(zhǔn)備工作,比如說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工廠,走進(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.
          ?????}
          ???}
          ?}

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

          映射器Mapper文件的解析

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

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

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

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

          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)代理類
          ??}
          ??parsePendingResultMaps();
          ??parsePendingCacheRefs();
          ??parsePendingStatements();
          }
          • configurationElement(XNode context)方法

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

          configurationElement

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

          • bindMapperForNamespace()方法

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

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

          bindMapperForNamespace方法源碼如下所示:

          private?void?bindMapperForNamespace()?{
          ???//獲取mapper元素的namespace屬性值
          ???String?namespace?=?builderAssistant.getCurrentNamespace();
          ???if?(namespace?!=?null)?{
          ?????Class?boundType?=?null;
          ?????try?{
          ???????//?獲取namespace屬性值對應(yīng)的Class對象
          ???????boundType?=?Resources.classForName(namespace);
          ?????}?catch?(ClassNotFoundException?e)?{
          ???????//如果沒有這個(gè)類,則直接忽略,這是因?yàn)閚amespace屬性值只需要唯一即可,并不一定對應(yīng)一個(gè)XXXMapper接口
          ???????//沒有XXXMapper接口的時(shí)候,我們可以直接使用SqlSession來進(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屬性值有對應(yīng)的Java類,調(diào)用Configuration的addMapper方法,將其添加到MapperRegistry中
          ?????????configuration.addMapper(boundType);
          ???????}
          ?????}
          ???}
          ?}

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

          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)類
          • addMapper(): mybatis在解析配置文件時(shí),會(huì)將需要生成動(dòng)態(tài)代理類的接口注冊到其中

          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ì)針對其生成動(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)代理類
          ??????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è)接口對應(yīng)一個(gè)MapperProxyFactory(生成動(dòng)態(tài)代理工廠類)

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

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

          2. Configuration#getMapper()

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

          public??T?getMapper(Class?type,?SqlSession?sqlSession)?{
          ??//根據(jù)Class對象獲取創(chuàng)建動(dòng)態(tài)代理的工廠對象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è)新的代理對象返回
          ????return?mapperProxyFactory.newInstance(sqlSession);
          ??}?catch?(Exception?e)?{
          ????throw?new?BindingException("Error?getting?mapper?instance.?Cause:?"?+?e,?e);
          ??}
          }

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

          protected?T?newInstance(MapperProxy?mapperProxy)?{
          ??//這里使用JDK動(dòng)態(tài)代理,通過Proxy.newProxyInstance生成動(dòng)態(tài)代理類
          ??// newProxyInstance的參數(shù):類加載器、接口類、InvocationHandler接口實(shí)現(xiàn)類
          ??//?動(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ì)說了,有興趣的可以參閱我之前寫的文章:動(dòng)態(tài)代理的原理及其應(yīng)用

          這里的InvocationHandler接口的實(shí)現(xiàn)類是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類中定義的方法,直接通過反射調(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對象,然后掉用其execute方法真正的開始執(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類的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語句的處理邏輯
          ??????case?INSERT:?{
          ????????Object?param?=?method.convertArgsToSqlCommandParam(args);
          ????????result?=?rowCountResult(sqlSession.insert(command.getName(),?param));
          ????????break;
          ??????}
          ??????//update語句的處理邏輯
          ??????case?UPDATE:?{
          ????????Object?param?=?method.convertArgsToSqlCommandParam(args);
          ????????result?=?rowCountResult(sqlSession.update(command.getName(),?param));
          ????????break;
          ??????}
          ??????//delete語句的處理邏輯
          ??????case?DELETE:?{
          ????????Object?param?=?method.convertArgsToSqlCommandParam(args);
          ????????result?=?rowCountResult(sqlSession.delete(command.getName(),?param));
          ????????break;
          ??????}
          ??????//select語句的處理邏輯
          ??????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)部類,SqlCommand和MethodSignature類,在execute方法中首先用switch case語句根據(jù)SqlCommand的getType()方法,判斷要執(zhí)行的sql類型,比如INSET、UPDATE、DELETE、SELECT和FLUSH,然后分別調(diào)用SqlSession的增刪改查等方法。

          慢著,說了這么多,那么這個(gè)getMapper()方法什么時(shí)候被調(diào)用呀?實(shí)際是一開始我們調(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)我們需要注意:我們通過SqlSession的getMapper方法獲得接口代理來進(jìn)行CRUD操作,其底層還是依靠的是SqlSession的使用方法

          小結(jié)

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

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

          參考資料 & 鳴謝

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

          往期推薦

          如何用 SpringBoot 實(shí)現(xiàn) MySQL 的讀寫分離?

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

          用 Arthas 定位 Spring Boot 接口的超時(shí)問題,讓應(yīng)用起飛~

          JVM 內(nèi)存工具匯總,建議收藏!


          下方二維碼關(guān)注我

          技術(shù)草根堅(jiān)持分享?編程,算法,架構(gòu)

          朋友助力下!點(diǎn)個(gè)在看
          瀏覽 90
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  免费 69视频看片 | 凹凸性爱视频 | 色老板最新凹凸视频 | 俺来也俺去啦欧美www | 亚洲综合无码一区二区毛片 |