從上文<MyBatis框架中Mapper映射配置的使用及原理解析(六) MapperRegistry> 中咱們知道DefaultSqlSession的getMapper方法,最後是經過MapperRegistry對象得到Mapper實例:html
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。java
咱們看看這個knownMapper在MapperRegistry中的定義: sql
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
有getMapper方法,那麼必而後addMapper方法:apache
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()方法session
//綁定到命名空間 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); } } } }
咱們看到 app
configuration.addMapper(boundType);
正是調用MapperRegistry.addMapper方法框架
public <T> void addMapper(Class<T> type) { mapperRegistry.addMapper(type); }
咱們在回過頭來看getMapper是如何得到Mapper對象的: ide
1.先獲取MapperProxyFactorypost
2.再調用MapperProxyFactory對象的newInstance方法得到Mapper。ui
咱們看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方法。