array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 Mybatis的Mapper接口的动态代理机制 - 爱码网

Mybatis源码中Mapper的动态代理实现原理

现在工作中用的最多的就是Mybatis这款半自动ORM框架,用的久却对其了解不是很深,现在准备对其进行一些深入的学习,顺便对知识进行查漏补缺.本篇是对Mapper动态代理原理的详解.


代理模式定义

为另一个对象提供一个替身或者占位符以控制对这个对象的访问.也就是说目的是控制对象行驶他的职责.当然也可以增强其职责,比如Spring AOP.

代理模式类图

由下图分析,代理模式所需要的角色为:

  1. 对外的行为接口Subject,对于调用方Client可见

  2. RealSubject真实的Subject,其包含具体的接口行为,对于Client不可见

  3. 代理类Proxy,其是RealSubject的替身,也可以当成对RealSubject的一层包装,对于Client不可见. 
    Mybatis的Mapper接口的动态代理机制

JDK动态代理实例

案例采取Java的动态代理形式开发,按照上述类图定义角色 
Subject接口 
public interface Subject { 
/** 
* 反转输入的input字符串 
* @param input 要反转的串 
* @return 反转后的串 
*/ 
String reversalInput(String input); 

RealSubject对接口的实现 
public class RealSubject implements Subject { 
public String reversalInput(String input) { 
System.out.println(“我是RealSubject: “+input); 
return new StringBuilder(input).reverse().toString(); 


SubjectProxy代理方法类 
该类实现了InvocationHandler,实际上是对调用的拦截,拦截后转向真实对象的调用,从而拿到正确的结果.是不是很像装饰者模式?其实也可以这样理解,设计模式之前本身就有很多关联性,不需要认定某一个行为就是单一的某个模式,从产生效果来看这里的SubjectProxy实际上就是对RealSubject的装饰,只不过这个装饰并没有添加新功能.

public class SubjectProxy implements InvocationHandler { 
private Object target; 
public SubjectProxy(Object target) { 
this.target = target; 

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
System.out.println(“proxy subject is “+proxy.getClass()); 
System.out.println(“real subject : “+ToStringBuilder.reflectionToString(target)); 
System.out.println(“method: “+method); 
System.out.println(“args: “+ ToStringBuilder.reflectionToString(args)); 
return method.invoke(target, args); 


Client调用

public class Client { 
public static void main(String[] args) { 
RealSubject subject = new RealSubject(); 
Subject proxyInstance = (Subject) Proxy.newProxyInstance( 
Subject.class.getClassLoader(), 
new Class[]{Subject.class}, 
new SubjectProxy(subject)); 
System.out.println(proxyInstance.reversalInput(“hello world”)); 


输出

proxy subject is com.sun.proxy.$Proxy0 
real subject: [email protected][] 
method: public abstract java.lang.String cn.mrdear.proxy.Subject.reversalInput(java.lang.String) 
args: [Ljava.lang.Object;@29444d75[{hello world}] 
我是RealSubject: hello world 
dlrow olleh

分析 
1.动态代理哪里体现了动态?

对于常规Java类变量创建要求有.java文件,然后编译成.class文件,然后虚拟机加载该.class文件,最后才能生成对象.但是对于Subject proxyInstance该代理类其是不存在.java文件的,也就是该对象的.class文件是动态生成的,然后虚拟机加载该class文件,创建对象.在Proxy.java中有如下代码动态生成class文件,这里不多深入(因为我也不懂~~~).

/* 
* Generate the specified proxy class. 
*/ 
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

2.JDK动态代理的要求

JDK动态代理只能针对接口,如果要针对普通类则可以考虑CGLib的实现,这里不多分析.其次动态代理的要求有接口类Subject,InvocationHandler代理方法类存在,才能创建出代理对象,代理对象的执行方法都被InvocationHandler接口所拦截,转向真实类的执行或者你想要的操作.

Mybatis的动态代理Mapper

由上面内容可以看出JDK动态代理需要接口,真实实现类,Clinet调用方,在常规的Mybatis的Mapper代理中接口就是Mapper(Dao中的接口方法),Client是service,那么真实的实现类是什么?显而易见这里就是Mapper代理的关键点.

MapperProxyFactory

顾名思义该类是产生Mapper接口的工厂类,其内部有如下方法,由此可以看出MapperProxy是方法拦截的地方,那么到此动态代理所需要的必须角色都以凑齐,那么接下来分析最重要的MapperProxy方法拦截.

protected T newInstance(MapperProxy<T> mapperProxy) { 
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); 
}

MapperProxy

该类是Mapper接口的Proxy角色,继承了InvocationHandler,所以具有方法拦截功能,看代码注释.

@Override 
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
try { 
if (Object.class.equals(method.getDeclaringClass())) { ////判断是否为object,因为其不是接口 
return method.invoke(this, args); 
} else if (isDefaultMethod(method)) { //判断是否为接口总的默认方法,jdk8允许接口中声明默认方法. 
return invokeDefaultMethod(proxy, method, args); 

} catch (Throwable t) { 
throw ExceptionUtil.unwrapThrowable(t); 

//对正常Mapper请求的处理 
final MapperMethod mapperMethod = cachedMapperMethod(method); 
return mapperMethod.execute(sqlSession, args); 

对于正常的Mapper接口中的方法调用,mybatis都会转向到MapperMethod的execute方法中执行,拿到结果返回给调用方Client,整个代理过程结束.对于正常调用是有缓存的,并且该代理类是项目启动时就生成好的,对于性能影响并不是很大实用性还是很高的.

这里要注意下对于默认接口方法的处理invokeDefaultMethod(proxy, method, args),该方法中每次都直接生成代理类,对性能是一种损耗应该不小,所以不建议在Mapper接口中写默认方法. 
@UsesJava7 
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) 
throws Throwable { 
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class 
.getDeclaredConstructor(Class.class, int.class); 
if (!constructor.isAccessible()) { 
constructor.setAccessible(true); 

final Class<?> declaringClass = method.getDeclaringClass(); 
return constructor 
.newInstance(declaringClass, 
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED 
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC) 
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); 
}

总结

从上面来看动态代理的最大的好处就是接口与其实现类的解耦,原本接口和动态类之间是强关联状态,接口不能实例化,实现类必须实现接口的所有方法,有了动态代理之后,接口与实现类的关系并不是很大,甚至不需要实现类就可以完成调用,比如Mybatis这种形式,其并没有创建该接口的实现类,而是用一个方法拦截器转向到自己的通用处理逻辑. 
另外就是Spring AOP的动态代理,解耦后自然可以实现对原有方法增强的同时又对其代码的零侵入性. 
    Mybatis的Mapper动态代理实现原理还是很清晰的,很多东西还需要不断的积累才能更明白。

相关文章: