從上圖可能看出,在 mybatis中,SqlSession的實現類有兩個,其中SqlSessionManager類不但實現了SqlSession接口,同時也實現了SqlSessionFactory接口。那么SqlSessionManager類究竟有何作用 ? 由於源碼中缺少注釋,所以從mybatis目前的提供官方文檔來看,似乎該類已被棄用,其功能被DefaultSqlSession類和DefaultSqlSessionFactory類所代替。只是該類的部分代碼對我們理解mybatis的一些底層機制還具有一定的參考價值,例如:
SqlSessionManager的下面的構造方法,會產生一個SqlSession的一個代理對象:
private SqlSessionManager(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionInterceptor()); }
SqlSessionInterceptor類實現了InvocationHandler接口
privaprivate class SqlSessionInterceptor implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get(); if (sqlSession != null) { try { return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } else { final SqlSession autoSqlSession = openSession(); try { final Object result = method.invoke(autoSqlSession, args); autoSqlSession.commit(); return result; } catch (Throwable t) { autoSqlSession.rollback(); throw ExceptionUtil.unwrapThrowable(t); } finally { autoSqlSession.close(); } } } } private class SqlSessionInterceptor implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { final SqlSession sqlSession = SqlSessionManager.this.localSqlSession.get(); if (sqlSession != null) { try { return method.invoke(sqlSession, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } else { final SqlSession autoSqlSession = openSession(); try { final Object result = method.invoke(autoSqlSession, args); autoSqlSession.commit(); return result; } catch (Throwable t) { autoSqlSession.rollback(); throw ExceptionUtil.unwrapThrowable(t); } finally { autoSqlSession.close(); } } } }
下面對這一段使用JAVA動態代理技術產生SqlSession代理對象的代碼進行分析:
this.sqlSessionProxy = (SqlSession) Proxy.newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[]{SqlSession.class},
new SqlSessionInterceptor()) 這句是關鍵,JDK的Proxy類的newProxyInstance方法的方法原型如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
在調這個方法中需要傳入三個參數:
Ø 一個interfaces的數組參數
Ø 一個InvocationHanler 接口的實例對象
Ø 一個類加載器,
則Proxy.newProxyInstance方法執行后會返回interfaces中任一接口的實例對象(假設該對象為proxyObject),那么當我們在調用這個對象proxyObject的相應方法時,就會進入到InvocationHandler 這個參數對象的invoke(Object proxy, Method method, Object[] args)方法中,或者換句話說,就會被h這個對象的invoke方法攔截, 對象proxyObject會作為
Invoke中的proxy參數,proxyObject調用的方法的方法對象會作為method參數,方法的參數會作為args參數,這樣在InvocationHandler 對象的invoke方法中,就會通過Method.invoke方法來執行具體的目標對象的相應方法,在mybatis的這個應用場景上,這個目標對象其實就是一個SqlSession的實例,通過SqlSessionManager類的成員變量sqlSessionFactory的openSession()獲得或者從當前線程中獲取。
以上的實現技術主要就是使用了java的動態代理技術,看到網上不少人在問這個InvocationHandler 接口中的invoke方法的第一個參數proxy究竟有何作用,這個proxy其實就是一個代理對象實例(通過Proxy.newProxyInstance方法產生),下面就舉例說明一下它的作用:
可參照 java.rmi.server.RemoteObjectInvocationHandler類中的相應方法invoke方法,一個用法就是判斷invoke的method參數,看是否有必要調用proxy對象的其他方法,另一個用處就是作為參數把該對象提供給遠程調用的方法使用。