mybatis執行原始碼
阿新 • • 發佈:2020-09-19
Mybatis可以把Mapper.xml檔案直接對映到對應的介面,呼叫介面方法會自動去Mapper.xml檔案中找到對應的標籤,這個功能就是利用java的動態代理在binding包中實現的。
一、註冊Mapper
在初始化時會把獲取到的Mapper介面註冊到MapperRegistry,註冊的時候建立一個Mapper代理工廠,這個工廠通過JDK的代理建立一個執行物件,建立代理需要的InvocationHandler為MapperProxy
public class MapperRegistry { public <T> T getMapper(Class<T> type, SqlSession sqlSession) {//取出MapperProxyFactory final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) throw new BindingException("Type " + type + " is not known to the MapperRegistry."); try {//建立代理 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } } //建立代理的工廠 public class MapperProxyFactory<T> { /** * 需要建立代理的介面 */ privatefinal Class<T> mapperInterface; /** * 執行方法的快取,不需要每次都建立MapperMethod */ private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { //建立代理, InvocationHanderl是MapperProxy return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } /** * 傳人sqlSession建立代理 * @param sqlSession * @return */ public T newInstance(SqlSession sqlSession) { //把代理執行需要用到的物件傳入 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
二、獲取介面物件
從knownMappers中根據介面型別取出對應的代理建立工廠,用該工廠建立代理。
-
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; 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; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果方法是Object裡面的則直接呼叫方法 if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } //獲取執行方法的封裝物件 final MapperMethod mapperMethod = cachedMapperMethod(method); //裡面就是找到對應的sql 執行sql語句 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; } }
三、呼叫介面方法
呼叫代理方法會進入到MapperProxy的public Object invoke(Object proxy, Method method, Object[] args)方法
-
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; 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; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //如果方法是Object裡面的則直接呼叫方法 if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } //獲取執行方法的封裝物件 final MapperMethod mapperMethod = cachedMapperMethod(method); //裡面就是找到對應的sql 執行sql語句 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; } }
最終執行sql會進入到MapperMethod中execute方法:
-
public class MapperMethod { private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { //SqlCommand封裝該介面方法需要執行sql的相關屬性,如:id(name), 型別 this.command = new SqlCommand(config, mapperInterface, method); //執行方法特性進行封裝,用於構造sql引數,判斷執行sql邏輯走哪條分支 this.method = new MethodSignature(config, method); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; //先找到對應的執行sql型別, sqlSession會呼叫不同方法 if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) {//如果是查詢, 需要對返回做判斷處理 //根據方法的特性判斷進入哪個執行分支 if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { //只查一條資料 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else { throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
}
上面就是根據介面、方法、配置引數找到對應的執行sql,並構造引數,解析執行結果,具體sql執行在sqlSession流程裡面,後面再看。