MyBatis框架中Mapper映射配置的使用及原理解析MapperProxy,MapperProxyFactory

学向勤中得,萤窗万卷书。这篇文章主要讲述MyBatis框架中Mapper映射配置的使用及原理解析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); }

 
【MyBatis框架中Mapper映射配置的使用及原理解析MapperProxy,MapperProxyFactory】我们在回过头来看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方法。
 

    推荐阅读