(很早就接触了动态代理,出于不知道这种模式的实用意义到底在哪里,所以每次简单了解完代理的过程不久也忘了。但是知道了mybatis就是利用动态代理来生成mapper实例,借此再总结一次)
JDK动态代理只能针对接口(所以声明Mapper接口),如果要针对普通类则可以考虑CGLib的实现。
平时使用mybatis基本都是使用接口声明,然后注入直接调用接口方法,即可完成数据库操作。有没有疑惑过,为什么没看到实现该接口的类,就可以调用方法。要知道接口是不能实例化的。这是对代理类生成(代理模式)的理解。
对于常规Java类变量的创建要求有 .java 文件,然后编译成 .class 文件,然后虚拟机加载该 .class 文件,最后才能生成对象。但是对Subject proxyInstance(某个代理实例)该代理类其是不存在 .java 文件的,也就是该对象的 .class 文件是动态生成的,然后虚拟机加载该class文件,然后创建对象。
可知JDK动态代理需要接口,真实实现类,Client调用方。在常规的Mybatis的Mapper代理中接口就是Mapper,Client是service,那么真实的实现类是什么?
这里就是Mapper代理的关键点。
从关键的部分开始解读源码:
1、MapperRegistry(Mapper注册器)
MapperRegistry类是注册Mapper接口与获取代理类实例的工具类。
该工具类里头有很明显的执行链:
首先是addMappers(String packageName)方法,该方法通过包名扫描下面所有接口。
该方法内就是对addMappers(重载方法)的调用,该方法只是个中间过程,方法内通过循环执行addMapper(mapperClass)方法。
addMapper对每一个扫描到的接口( if (type.isInterface()) ,接口类也是Class类型,编译后也会生成相应的.class文件 )放进knownMappers
中,knownMappers是一个HashMap,Key是mapper的类型对象, Value是MapperProxyFactory对象。
接着(某处?)调用getMapper方法,方法里面最后会去调用MapperProxyFactory类的newInstance方法。getMapper方法前会初始化
MapperProxyFactory,它是创建Mapper代理对象的工厂。
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//通过Mapper的接口类型 去Map当中查找 如果为空就抛异常
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null)
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
try {
//否则创建一个当前接口的代理对象 并且传入sqlSession
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
2、MapperProxyFactory(创建Mapper代理对象的工厂)
该工厂内有两个同名的重载方法,执行链是先通过newInstance(SqlSession sqlSession)方法获得上方所说的真实的实现类mapperProxy,去调用
newInstance(MapperProxy<T> mapperProxy)方法。最后就是一直以来所熟悉的的JDK动态代理return (T) Proxy.newProxyInstance
(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
所以重点回到获得真实的实现类MapperProxy上
3、MapperProxy(mapper的实际代理对象)
这个类实现了JDK的动态代理接口InvocationHandler。
该类中有一个methodCache对象,是接口中方法的缓存。
从上方第一点的getMapper方法就是用来获得相关的数据操作类接口。而事实数据操作类邦定了动态代理。所以操据操作类执行方法的时候,会触动
每个方法相应的MapperProxy类的invoke方法。
该方法从缓存中获得MapperMethod实例,通过mapperMethod.execute(sqlSession, args)真正对一个方法执行,可以看出这个方法对才是真正对
SqlSession进行的包装调用。
4、MapperMethod是真正代理一条mybatis mapper标签的方法,要知道动态代理只是对一个方法作代理。
类里面有俩个成员:SqlCommand类和MethodSignature类。一个跟SQL语句有关系,一个跟要执行的方法有关系。
并使用一个内部类SqlCommand来封装底层的增删改查操作,确切来讲这一部分的内容跟XxxMapper的XML配置文件里面的select节点、delete节点等
有关。我们都会知道节点上有id属性值。那么MyBatis框架会把每一个节点(如:select节点、delete节点)生成一个MappedStatement类。要找到
MappedStatement类就必须通过id来获得。
参考:
https://blog.csdn.net/lom9357bye/article/details/80207074
https://blog.csdn.net/xiaokang123456kao/article/details/76228684