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方法。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM