knownMappers.put(type, new MapperProxyFactory<T>(type));
type 就是 Dao 介面,下面來看 MapperProxyFactory 類。public class MapperProxyFactory<T> { private final Class<T> mapperInterface; 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) { 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); } }MapperProxyFactory 看名字就知道這是一個工廠類,目的就是為了生成 MapperProxy。下面我們主要看它的構造方法和 newInstance() 方法。
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 { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); 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 動態代理,MapperProxy 實現了 InvocationHandler 介面。下面先看 invoke() 方法。
public interface SqlSession extends Closeable { <T> T selectOne(String statement); <T> T selectOne(String statement, Object parameter); // 下面省略 }這個介面方法的入參是 statement 和引數(parameter),返回值是資料物件。statement 可能會被誤解為 SQL 語句,但其實這裡的 statement 是指 Dao 介面方法的名稱,自定義的 SQL 語句都被快取在 Configuration 物件中。
public class MapperMethod{ private final SqlCommand command; private final MethodSignature method; }MapperMethod 類中有兩個成員變數,分別是 SqlCommand 和 MethodSignature。雖然這兩個類的程式碼看起來很多,但實際上這兩個內部類非常簡單。
public Object execute(SqlSession sqlSession, Object[] args) { Object param; Object result; switch(this.command.getType()) { case INSERT: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case UPDATE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); break; case DELETE: param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); break; case SELECT: if (this.method.returnsVoid() && this.method.hasResultHandler()) { // 返回型別為void this.executeWithResultHandler(sqlSession, args); result = null; } else if (this.method.returnsMany()) { // 返回型別為集合或陣列 result = this.executeForMany(sqlSession, args); } else if (this.method.returnsMap()) {// 由@MapKey控制返回 result = this.executeForMap(sqlSession, args); } else if (this.method.returnsCursor()) {// 返回型別為Cursor<T>,採用遊標 result = this.executeForCursor(sqlSession, args); } else { // 其他型別 param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); } break; case FLUSH: result = sqlSession.flushStatements(); break; default: throw new BindingException("Unknown execution method for: " + this.command.getName()); } if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) { throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ")."); } else { return result; } }通過上面對 SqlCommand 和 MethodSignature 的簡單分析,我們很容易理解這段程式碼。