从上图可能看出,在 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 对象的其他方法,另一个用处就是作为参数把该对象提供给远程调用的方法使用。