Mybatis动态代理解析
1、引言
Mybatis作为一种持久层框架,它支持自定义SQL、存储过程以及高级映射。里面大量使用了代理技术,这篇文章将从以下几个问题对Mybatis中的代理进行解析。
MybatisMapper接口在无实现类的情况下,如何实现的动态代理。
JDK动态代理为什么不能对非接口类进行代理。2、JDK动态代理2。1、基于接口的动态代理
代码实例publicinterfaceStudentDao{publicvoidsayHello();}publicclassStudentDaoImplimplementsStudentDao{OverridepublicvoidsayHello(){System。out。println(你好);}}publicclassProxyHandlerimplementsInvocationHandler{privateObjecttarget;publicProxyHandler(Objecttarget){this。targettarget;}OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable{System。out。println(调用代理类);returnmethod。invoke(target,args);}publicstaticvoidmain(String〔〕args){StudentDaostudentDaonewStudentDaoImpl();StudentDaoproxy(StudentDao)Proxy。newProxyInstance(studentDao。getClass()。getClassLoader(),studentDao。getClass()。getInterfaces(),newProxyHandler(studentDao));proxy。sayHello();}}
上述代码运行后,可以发现对应proxy对象有一个特殊标记(Proxy拼接一个数字),这代表该对象的类型是一个代理对象。那么这个代理类是如何生成的,这些标记是如何构建的,下面我们走入Proxy。newProxyInstance里面去一瞅真相。
newProxyInstance
该方法主要是生成代理类,其内部主要的流程为getProxyClass0与newInstance,其时序图如下所示:
1。getProxyClass0会创建代理类
2。newInstance会绑定代理类和增强类的关系
3、Mybatis中的动态代理
Mybatis未出现之前,开发人员需要在每个mapper层的实现类中重复进行如下操作,对数据库进行增删查改。装载Mysql驱动Class。forName(driveName);获取连接conDriverManager。getConnection(url,user,pass);创建StatementStatementstatecon。createStatement();构建SQL语句StringstuQuerySqlStrSELECTFROMSTUDENT;执行SQL返回结果ResultSetresultstate。executeQuery(stuQuerySqlStr);
Mybatis针对该痛点,利用JDK动态代理对以上逻辑进行封装,使用者只需要自定义Mapper和xml文件,利用注解或者xml的形式定义SQL语句,项目启动时通过解析器解析SQL语句组装为Java中的对象。
但是日常使用Mybatis过程中我们都是直接定义的mapper接口,没有定义其实现类,那么JDK动态代理是如何代理未实现的接口呐,和上文具有实现类的接口代理有什么区别?
未实现类接口的动态代理publicinterfaceSubject{StringsayHello();}publicclassProxyInvocationHandlerimplementsInvocationHandler{OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object〔〕args)throwsThrowable{System。out。println(进入代理调用处理器);returnsuccess;}}
可以发现对于未实现类接口的动态代理,其InvocationHandler并未执行代理类的方法,而是执行完增强方法后就返回InvocationHandler的返回值;而具有实现类接口的动态代理则会执行代理类方法并且返回其执行结果。
那么,Mybatis对于maaper接口的代理方法是怎样的呐,其InvocationHandler的具体实现细节是什么?我们接着分析。
MapperProxyFactory
该类是通过工厂模式去生成mapper代理类,其中mapperInterface是等待生成代理对象的接口。publicclassMapperProxyFactoryT{privatefinalClassTmapperInterface;privatefinalMapMethod,MapperMethodInvokermethodCachenewConcurrentHashMap();publicMapperProxyFactory(ClassTmapperInterface){this。mapperInterfacemapperInterface;}publicClassTgetMapperInterface(){returnmapperInterface;}publicMapMethod,MapperMethodInvokergetMethodCache(){returnmethodCache;}SuppressWarnings(unchecked)protectedTnewInstance(MapperProxyTmapperProxy){return(T)Proxy。newProxyInstance(mapperInterface。getClassLoader(),newClass〔〕{mapperInterface},mapperProxy);}publicTnewInstance(SqlSessionsqlSession){finalMapperProxyTmapperProxynewMapperProxy(sqlSession,mapperInterface,methodCache);returnnewInstance(mapperProxy);}}
这里我们重点关注该类中的两个newInstance方法,由于mapper是一个接口,最终代理类的调用是执行的InvocationHandler中的invoke方法,所以最终的mybatis封装mapper的操作放在MapperProxy中。我们接着看里面的细节。
MapperProxy
由于该类代码较多,我们这里只关注封装部分的方法逻辑,该部分逻辑获取对应mapper中的方法的增强实现,并调用其invoke方法。MapperMethod类是整个代理机制的核心类,对SqlSession中的操作进行了封装使用。该类里有两个内部类SqlCommand和MethodSignature。SqlCommand用来封装CRUD操作,也就是我们在xml中配置的操作的节点。每个节点都会生成一个MappedStatement类。MethodSignature用来封装方法的参数以及返回类型privateMapperMethodInvokercachedInvoker(Methodmethod)throwsThrowable{try{Aworkaroundforhttps:bugs。openjdk。java。netbrowseJDK8161372ItshouldberemovedoncethefixisbackportedtoJava8orMyBatisdropsJava8support。Seegh1929MapperMethodInvokerinvokermethodCache。get(method);if(invoker!null){returninvoker;}returnmethodCache。computeIfAbsent(method,m{if(m。isDefault()){try{if(privateLookupInMethodnull){returnnewDefaultMethodInvoker(getMethodHandleJava8(method));}else{returnnewDefaultMethodInvoker(getMethodHandleJava9(method));}}catch(IllegalAccessExceptionInstantiationExceptionInvocationTargetExceptionNoSuchMethodExceptione){thrownewRuntimeException(e);}}else{returnnewPlainMethodInvoker(newMapperMethod(mapperInterface,method,sqlSession。getConfiguration()));}});}catch(RuntimeExceptionre){Throwablecausere。getCause();throwcausenull?re:cause;}}
MapperMethod
该类是整个代理机制的核心类,对SqlSession中的操作进行了封装使用。从而实现了CRUD,其最后还是回到SqlSession中进行调用数据库。ublicObjectexecute(SqlSessionsqlSession,Object〔〕args){Objectresult;switch(command。getType()){caseINSERT:{Objectparammethod。convertArgsToSqlCommandParam(args);resultrowCountResult(sqlSession。insert(command。getName(),param));break;}caseUPDATE:{Objectparammethod。convertArgsToSqlCommandParam(args);resultrowCountResult(sqlSession。update(command。getName(),param));break;}caseDELETE:{Objectparammethod。convertArgsToSqlCommandParam(args);resultrowCountResult(sqlSession。delete(command。getName(),param));break;}caseSELECT:if(method。returnsVoid()method。hasResultHandler()){executeWithResultHandler(sqlSession,args);resultnull;}elseif(method。returnsMany()){resultexecuteForMany(sqlSession,args);}elseif(method。returnsMap()){resultexecuteForMap(sqlSession,args);}elseif(method。returnsCursor()){resultexecuteForCursor(sqlSession,args);}else{Objectparammethod。convertArgsToSqlCommandParam(args);resultsqlSession。selectOne(command。getName(),param);if(method。returnsOptional()(resultnull!method。getReturnType()。equals(result。getClass()))){resultOptional。ofNullable(result);}}break;caseFLUSH:resultsqlSession。flushStatements();break;default:thrownewBindingException(Unknownexecutionmethodfor:command。getName());}if(resultnullmethod。getReturnType()。isPrimitive()!method。returnsVoid()){thrownewBindingException(Mappermethodcommand。getName()attemptedtoreturnnullfromamethodwithaprimitivereturntype(method。getReturnType())。);}returnresult;}
助听器的纯音测听的方法是什么?您好,助听器的纯音测听是借助专业的测听工具,对测试者进行气导骨导和大声的测试,以此获取听阈和不舒适阈,根据检查的数据进行助听器的科学验配,建议直接去专业正规的验配中心进行测试即可,
王者荣耀,凯尼禄皮肤,宫本但丁皮肤,廉颇绿巨人,墨子钢铁侠,大家觉得合适不?大家好,这里是高进说游戏。关于这个问题,提主想表达的应该是他说的那些英雄出对应的皮肤是否可行的问题。提主说的尼禄和但丁应该是鬼泣中的角色,绿巨人和钢铁侠大家应该熟悉了,这两个是漫威
当醋遇上花生黑豆和姜,会有怎样的养生效果?谢谢邀请。我刚好做了一个醋泡黑豆,之前的泡好了吃了快俩月了,今天早晨吃完,刚把黑豆洗干净用白醋泡好,我先说一下醋泡黑豆,我的亲身体会就是血压稳定多了,头也疼的少了。我之前也泡过黑豆
如何看待曾经不起眼的快餐皮肤如今玩家想要却太难?大家好,我是北途王者已经19赛季了,这几年出皮肤马上300款了。然后免费的也有,6元的也有,28的也有,38的也有,48的也有,58的也有,68的也有更多多多,甚至200多的也有。
医疗基金还有希望吗?医疗板块希望很大,你们觉得没有希望之时,就是希望开始之时。第一,医疗板块下跌空间比较大,空间上已经足够。医疗板块目前跌幅20多个点,一般一个板块跌幅超过20个点,基本上离底部不远了
低盘发和高盘发哪种更好看?对于大部分人来说,都是可高可低的。这就好比一个人可以穿衬衣也可以穿T恤还可以穿西装是一个道理。底盘发还是高盘发主要是看你盘发的目的,然后就是服装的搭配,也就是你到底想达到什么感觉和
黑色羽绒服配米色马丁靴好看吗?现在正是三九天的季节,非常的冷,所以人们非常喜欢穿黑色的羽绒服,它不仅显瘦而且还比较耐脏。再配上一双米色的马丁靴,要多美有多美,既简单大方又美丽漂亮。打破黑色的暗沉黑色的羽绒服穿上
复古盘扣棉麻连衣裙搭什么鞋好看?人靠衣装马靠鞍这句老古话说得太到位了,别说是那些长得既漂亮身材又好的女生了,就连颜值一般的女生通过时尚服饰的装饰后又能散发出迷人的魅力,受欢迎程度不输那些让人羡慕的高颜值女生。当然
锁骨发好看吗,适合所有人吗?顾名思义,锁骨发不长不短,发尾刚好到戳到锁骨长度位置,锁骨发其实就是lob头的一种,也是最有个性的lob头,锁骨也是一款非常流行的发型,搭配起来也非常的好看,那么锁骨发适合什么脸型
为什么有些女生留短发就很好看,而有些人就不好看?短发的大风已经吹了好几年了,好像一直没有停下来的意思,好多明星和时尚达人都纷纷剪了短发,有的明星甚至因为剪了短发而人气大增,连衣品都提升了好几个度,当然,也有剪的不好看的。那为什么
中年妇女穿什么衣服好看?现在的中年妇女都时尚了,花花绿绿的都能穿!只要干净,得体,就很好看。1。应邀。中年妇女在穿衣打扮上遇到了很大的困扰,一方面她不希望自己变成大妈,穿那种松垮,休闲的中老年服装,她也不