MyBatis框架的使用及源码分析(七) MapperProxy,MapperProxyFactory
从上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中我们知道DefaultSqlSession的getMapper方法,最后是通过MapperRegistry对象获得Mapper实例:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) { //说明这个Mapper接口没有注册
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try { //生成一个MapperProxy对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
从代码中我们看到试图从一个叫knownMappers的变量取出MapperProxyFactory。
我们看看这个knownMapper在MapperRegistry中的定义:
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
有getMapper方法,那么必然后addMapper方法:
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
在<MyBatis框架中Mapper映射配置的使用及原理解析(四) 解析Mapper接口映射xml文件> 文章中,解析玩xml后,绑定命名空间bindMapperForNamespace()方法
//绑定到命名空间
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
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);
configuration.addMapper(boundType);
}
}
}
}
我们看到
configuration.addMapper(boundType);
正是调用MapperRegistry.addMapper方法
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
我们在回过头来看getMapper是如何获得Mapper对象的:
1.先获取MapperProxyFactory
2.再调用MapperProxyFactory对象的newInstance方法获得Mapper。
我们看MapperProxyFactory代码:
public T newInstance(SqlSession sqlSession) {
//创建一个Mapperxy对象,这个方法实现了JDK动态代理中的InvocationHandler接口
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
//mapperInterface,说明Mapper接口被代理了,这样子返回的对象就是Mapper接口的子类,方法被调用时会被mapperProxy拦截,也就是执行mapperProxy.invoke()方法
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
这里就是返回的一个代理类实例MapperProxy。
package org.apache.ibatis.binding; import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map; import org.apache.ibatis.reflection.ExceptionUtil;
import org.apache.ibatis.session.SqlSession; /**
* @author Clinton Begin
* @author Eduardo Macarron
*/
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
//Mapper接口
private final Class<T> mapperInterface;
/*
* Mapper接口中的每个方法都会生成一个MapperMethod对象, methodCache维护着他们的对应关系
*/
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
//这里会拦截Mapper接口的所有方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) { //如果是Object中定义的方法,直接执行。如toString(),hashCode()等
try {
return method.invoke(this, args);//
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method); //其他Mapper接口定义的方法交由mapperMethod来执行
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
要使用Java的动态代理就必须得实现InvocationHandler接口:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
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);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
首先判断代理对象是一个接口还是一个类,显然我们没有对mapper接口进行任何实现,那么它将执行
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
生成一个MapperMethod对象,接着调用其execute方法,把sqlSession和参数传递进去,执行Mapper方法。
MyBatis框架的使用及源码分析(七) MapperProxy,MapperProxyFactory的更多相关文章
- MyBatis框架的使用及源码分析(十一) StatementHandler
我们回忆一下<MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor> , 这4个Ex ...
- MyBatis框架的使用及源码分析(九) Executor
从<MyBatis框架的使用及源码分析(八) MapperMethod>文中我们知道执行Mapper的每一个接口方法,最后调用的是MapperMethod.execute方法.而当执行Ma ...
- MyBatis框架的使用及源码分析(八) MapperMethod
从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...
- MyBatis框架的使用及源码分析(六) MapperRegistry
我们先Mapper接口的调用方式,见<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用>的示例: public void findUserById() { Sql ...
- MyBatis框架的使用及源码分析(五) DefaultSqlSessionFactory和DefaultSqlSession
我们回顾<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 一文的示例 private static SqlSessionFactory getSessionF ...
- MyBatis框架的使用及源码分析(四) 解析Mapper接口映射xml文件
在<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 一文中,我们知道mybat ...
- MyBatis框架的使用及源码分析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder
在 <MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 的demo中看到了SessionFactory的创建过程: SqlSessionFactory sess ...
- MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor
Executor分成两大类,一类是CacheExecutor,另一类是普通Executor. 普通类又分为: ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情.它为每个语句的执行 ...
- MyBatis框架的使用及源码分析(三) 配置篇 Configuration
从上文<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 我们知道XMLConf ...
随机推荐
- vue学习笔记(三):vue-cli脚手架搭建
一:安装vue-cli脚手架: 1:为了确保你的node版本在4.*以上,输入 node -v 查看本机node版本,低于4请更新. 2:输入: npm install -g vue-cli ...
- Python中from module import *语法
from module import *的语法在Python 3.X和Python 2.X中的使用稍有区别: 在Python 3.X中,from module import *无法在函数里面使用,而在 ...
- 软件工程 speedsnail 第二次冲刺9
20150526 完成任务:划线的优化,速度和谐: 遇到问题: 问题1 速度仍然不满意 解决1 未解决 明日任务: 蜗牛碰到线后速度方向的调整:(做优化)
- P4语法(2) Parser
这里参考学习了: P4语言规范 P4台湾社群 Parser 关于parser 在P4程序中,有着大量的首部(header)和首部实例,但每次只有部分首部实例会对数据包进行操作,而parser会用于生成 ...
- java定时执行任务(一)
需求: 经常遇到这样的需求:要求每天执行一次任务,执行任务时间是凌晨3点 实现: 为了便于检测,我假设的是下一分钟执行任务,每10秒重复执行.(对应现实项目:每天3点执行任务.那么就是下一个3点执行任 ...
- python学习笔记07:自定义类型
class person: def __init__(self,name,age,weight): self.name = name self.age = age self.weight = weig ...
- lintcode-36-翻转链表 II
36-翻转链表 II 翻转链表中第m个节点到第n个节点的部分 注意事项 m,n满足1 ≤ m ≤ n ≤ 链表长度 样例 给出链表1->2->3->4->5->null, ...
- iOS-加载html字符串
NSMutableAttributedString * attrString =[[NSMutableAttributedString alloc] initWithData:[resultModel ...
- linux下清空文件全部内容,如log日志
在实际操作中经常需要清空log文件, 比如a.log, 有的人说, 直接删除不就行了, 但是, 直接删除后, 没法使用tail -f a.log了. 有的人说, 先rm再touch一个新文件不就可 ...
- [C/C++] C++模板定义格式
函数模板的格式: template <class 形参名,class 形参名,......> 返回类型 函数名(参数列表) { //函数体 } 类模板的格式为: template<c ...