从上文<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的更多相关文章

  1. MyBatis框架的使用及源码分析(十一) StatementHandler

    我们回忆一下<MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor> , 这4个Ex ...

  2. MyBatis框架的使用及源码分析(九) Executor

    从<MyBatis框架的使用及源码分析(八) MapperMethod>文中我们知道执行Mapper的每一个接口方法,最后调用的是MapperMethod.execute方法.而当执行Ma ...

  3. MyBatis框架的使用及源码分析(八) MapperMethod

    从 <MyBatis框架中Mapper映射配置的使用及原理解析(七) MapperProxy,MapperProxyFactory> 文中,我们知道Mapper,通过MapperProxy ...

  4. MyBatis框架的使用及源码分析(六) MapperRegistry

    我们先Mapper接口的调用方式,见<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用>的示例: public void findUserById() { Sql ...

  5. MyBatis框架的使用及源码分析(五) DefaultSqlSessionFactory和DefaultSqlSession

    我们回顾<MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 一文的示例 private static SqlSessionFactory getSessionF ...

  6. MyBatis框架的使用及源码分析(四) 解析Mapper接口映射xml文件

    在<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 一文中,我们知道mybat ...

  7. MyBatis框架的使用及源码分析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder

    在 <MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 的demo中看到了SessionFactory的创建过程: SqlSessionFactory sess ...

  8. MyBatis框架的使用及源码分析(十) CacheExecutor,SimpleExecutor,BatchExecutor ,ReuseExecutor

    Executor分成两大类,一类是CacheExecutor,另一类是普通Executor. 普通类又分为: ExecutorType.SIMPLE: 这个执行器类型不做特殊的事情.它为每个语句的执行 ...

  9. MyBatis框架的使用及源码分析(三) 配置篇 Configuration

    从上文<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 我们知道XMLConf ...

随机推荐

  1. JS原型与面向对象总结

    ECMAScript有两种开发模式:1.函数式(过程化),2.面向对象(OOP).面向对象的语言有一个标志,那就是类的概念,而通过类可以创建任意多个具有相同属性和方法的对象.但 是,ECMAScrip ...

  2. 定点数(fixed-point number)

    定义 定点数(fixed-point number)就是小数点位置固定的数,也就是说,小数点后面的位数是固定的,比如要记录一笔账目,这些账目的数字都不会超过100,就可以使用2位小数位定点数来记录,比 ...

  3. k邻近算法理解及代码实现

    github:代码实现 本文算法均使用python3实现 1 KNN   KNN(k-nearest neighbor, k近邻法),故名思议,是根据最近的 $ k $ 个邻居来判断未知点属于哪个类别 ...

  4. LintCode-50.数组剔除元素后的乘积

    数组剔除元素后的乘积 给定一个整数数组A. 定义B[i] = A[0] * ... * A[i-1] * A[i+1] * ... * A[n-1], 计算B的时候请不要使用除法. 样例 给出A=[1 ...

  5. C# Dsoframer.ocx 如何在winform中嵌入Excel,内嵌Excel,word

    如果你还不太清楚Dspframer.ocx怎么放到窗体上就看上一篇文章,里面详细介绍了是如何放到窗体上的. 链接:http://www.cnblogs.com/pingming/p/4182045.h ...

  6. 【week2】 四则运算改进

    四则运算满足简单加减乘除,以及包含括号的复杂四则运算. 代码描述: 1.采用random随机数产生要参与计算的数字,以及运算符号 2.采用Scanner获取控制台输入的结果,与计算出来的结果进行比对, ...

  7. ubuntu 16.04 安装jdk9错误

    转自:https://askubuntu.com/questions/769467/can-not-install-openjdk-9-jdk-because-it-tries-to-overwrit ...

  8. 向今天要结果; 向明天要动力 eclipse不自动弹出提示(alt+/快捷键失效)

    最近公司电脑上的Eclipse没有了自动提示功能,也不是全部不提示,大多数情况下按下“alt+/”键还会产生提示,但是当我在java项目中邪main方法和syso的时候,“alt+/”则会失效,今天在 ...

  9. [OS] 操作系统-进程线程-经典面试笔试题

    题目转自:http://blog.csdn.net/morewindows/article/details/7392749 ·线程的基本概念.线程的基本状态及状态之间的关系? 线程,有时称为轻量级进程 ...

  10. 【刷题】BZOJ 1002 [FJOI2007]轮状病毒

    Description 轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的.一个N轮状基由圆环上N个不同的基原子 和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道.如下 ...