在開始分析以前,先來了解一下這個模塊中的核心組件之間的關係,如圖:sql
MapperRegistry是Mapper接口及其對應的代理對象工程的註冊中心,Configuration是Mybatis全局性的配置對象,在初始化的過程當中,全部配置信息會被解析成相應的對象並記錄到Configuration對象中,這在以前也詳細介紹了。Configuration.mapperRegistry字段記錄當前使用的MapperRegistry對象,數組
public class MapperRegistry { // 全局惟一的配置對象,其中包含了全部的配置信息 private final Configuration config; // 記錄Mapper接口與對應MapperProxyFactory之間的關係 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>(); }
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); } } } }
public <T> void addMapper(Class<T> type) { mapperRegistry.addMapper(type); }
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)); // 註解處理 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
在須要執行SQL語句時,會先獲取mapper藉口的代理對象,例如:緩存
@Test public void findUserById() { SqlSession sqlSession = getSessionFactory().openSession(); UserDao userMapper = sqlSession.getMapper(UserDao.class); User user = userMapper.findUserById(1); Assert.assertNotNull("沒找到數據", user); }
DefaultSqlSession類中方法以下,其實是經過JDK動態代理生成的代理對象app
public <T> T getMapper(Class<T> type) { return this.configuration.getMapper(type, this); }
Configuration類方法以下:ide
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
MapperRegistry類中方法以下:ui
@SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //查找指定type對象的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 { // 建立實現了type接口的代理對象 return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
MapperProxyFactory主要負責建立代理對象this
@SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
MapperProxy實現了InvocationHandler接口,對動態代理的能夠先去了解這篇文章http://www.javashuo.com/article/p-aifratub-hk.htmlspa
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; // 記錄關聯的SQLSession對象 private final SqlSession sqlSession; // mapper接口對應的class對象 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; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 若是目標方法是Object類繼承來的,直接調用目標方法 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); } // 從緩存中獲取MapperMethod 對象,若是沒有就建立新的並添加 final MapperMethod mapperMethod = cachedMapperMethod(method); // 執行sql 語句 return mapperMethod.execute(sqlSession, args); } }
MapperMethod中封裝了Mapper接口中對應方法的信息,以及對應SQL語句的信息,.net
public class MapperMethod { // 記錄SQL語句的名稱和類型 private final SqlCommand command; // mapper接口中對應方法的相關信息 private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break; } case SELECT: if (method.returnsVoid() && method.hasResultHandler()) { // 處理返回值爲void ,ResultSet 經過ResultHand處理的方法 executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { // 處理返回值爲集合或者數組的方法 result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { // 處理返回值爲map的方法 result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) { // 處理返回值爲cursor的方法 result = executeForCursor(sqlSession, args); } else { // 處理返回值爲單一對象的方法 Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: 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; } }