SqlSession能够说是整个MyBatis的重中之重,在SqlSession中涉及到前一篇四大对象:Executor、StatementHandler、ParameterHandler、ResultHandler,因此在此先只对SqlSession有一个大概的了解。sql
在代码中咱们能够看到当咱们构造出一个SqlSession实例事后,能够经过SqlSession构造出Mappper映射器。UserMapper是一个接口,那么咱们能够确定的是,它必定是用了Java的动态代理生成了一个代理类。apache
SqlSession sqlSession = SessionFactory.getSqlSession(resource); UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
接着上一篇讲到经过DefaultSqlSessionFactory能够获得一个SqlSession的实例DefaultSqlSession。session
经过源代码能够发现,SqlSessionManger又实现了SqlSession,在上一节中可知SqlSessionManager一样也继承了SqlSessionFactory接口。咱们把它们结合起来看看。mybatis
看来这个SqlSessionManager有点神奇,同时继承SqlSession和SqlSessionFactory。从名字上来看好像是管理SqlSession的,这里不讨论,随着源代码的阅读相信咱们能逐步清晰,包括整个包的结构。app
回到DefaultSqlSession中来。在SqlSession接口中提供了不少方法,用于咱们的增删改查,这在旧版的MyBatis或者iBatis中经常所使用的,咱们如今大多直接使用xml配置文件以达到更加灵活的效果。因此咱们将注意力放在getMapper方法上。ide
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
注意UserMapper仅仅是一个接口,这里会涉及到Java的动态代理,因此得要有必定的基础才能读懂。this
经过打断点调试咱们能够发现确实产生了一个叫MapperProxy的代理类。spa
下面是DefaultSqlSession的getMapper方法:代理
//org.apache.ibatis.session.default.DefaultSqlSession
public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }
看起来语法有点奇怪,这是一个泛型方法。看来是调用了Configuration的getMapper方法,还不是DefaultSqlSession实现了getMapper。接着再看Configuration的getMapper方法:调试
//org.apache.ibatis.session.Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); }
Configuration.getMapper一共两个参数,一个是Class类型,一个是SqlSession,在DefaultSqlSession.getMapper调用Configuration.getMapper时,将传递进来的Class类型参数和其自己传递给了Configuration.getMapper。此时还不是在Configuration中实现了getMapper,看来仍是一个叫作mapperRegistry的变量。
//org.apache.ibatis.session.Configuration
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
看着名字好像是注册Mapper映射器的地方,想来也是,既然要获得Mapper的映射,那么全部的Mapper都要一个地方去注册(在咱们的mybytis-config.xml里),注册好事后须要的时候再去查找是否已经注册,那么就是MapperRegistry,因此取一个好的变量名是很是重要的。
1 //org.apache.ibatis.binding.MapperRegistry 2 public <T> T getMapper(Class<T> type, SqlSession sqlSession) { 3 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); 4 if (mapperProxyFactory == null) { 5 throw new BindingException("Type " + type + " is not known to the MapperRegistry."); 6 } 7 try { 8 return mapperProxyFactory.newInstance(sqlSession); 9 } catch (Exception e) { 10 throw new BindingException("Error getting mapper instance. Cause: " + e, e); 11 } 12 }
在第3行代码中试图从一个叫knownMappers的变量取出MapperProxyFactory。这个knownMapper的定义:
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
既然能用get方法取,那说明就有add方法咯?果不其然咱们在MapperRegistry类中发现了public <T> void addMapper(Class<T> type)方法,那么是在哪里调用的这个方法呢?
咱们来从新理一理。
使用MyBatis的第一步是配置mybatis-config.xml,配置好事后,mybatis-config跑起来的第一步也必定是首先解析xml配置文件,将解析好的配置文件各个配置参数放入Configuration对象中,包括Mapper的配置,因此应该是在解析xml文件的某个类中解析过来后调用Configuration的方法将mapper放置到MapperRegister中。事实也的确如此,有兴趣能够跟踪下代码看看。回到MapperRegistry.getMapper的方法中。
当咱们一切正确时,咱们就能获取到一个MapperProxyFactory实例。想必MapperProxy代理类的生成正是经过MapperProxyFactory工厂类构建的,即第8行代码。进入MapperProxyFactory类。
//org.apache.ibatis.binding.MapperProxyFactory public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); }
在这里终于看到了MapperProxy代理类,是经过sqlSession、mapperInterface、mechodCache三个参数构造的。
newInstance有一个重载方法:
//org.apache.ibatis.binding.MapperProxyFactory protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); }
终因而走到头了,这里就是返回的一个代理类实例。最后来看看MapperProxy。
MapperProxy是一个重要的类,因此咱们将其代码所有贴出:
1 //org.apache.ibatis.binding.MapperProxy 2 public class MapperProxy<T> implements InvocationHandler, Serializable { 3 4 private static final long serialVersionUID = -6424540398559729838L; 5 private final SqlSession sqlSession; 6 private final Class<T> mapperInterface; 7 private final Map<Method, MapperMethod> methodCache; 8 9 public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { 10 this.sqlSession = sqlSession; 11 this.mapperInterface = mapperInterface; 12 this.methodCache = methodCache; 13 } 14 15 @Override 16 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 17 if (Object.class.equals(method.getDeclaringClass())) { 18 try { 19 return method.invoke(this, args); 20 } catch (Throwable t) { 21 throw ExceptionUtil.unwrapThrowable(t); 22 } 23 } 24 final MapperMethod mapperMethod = cachedMapperMethod(method); 25 return mapperMethod.execute(sqlSession, args); 26 } 27 28 private MapperMethod cachedMapperMethod(Method method) { 29 MapperMethod mapperMethod = methodCache.get(method); 30 if (mapperMethod == null) { 31 mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); 32 methodCache.put(method, mapperMethod); 33 } 34 return mapperMethod; 35 } 36 37 }
要使用Java的动态代理就必须得实现InvocationHandler接口,在第17行代码中首先判断代理对象是一个接口仍是一个类,显然咱们没有对mapper接口进行任何实现,那么它将生成一个MapperMethod对象,接着调用其execute方法,把sqlSession和参数传递进去。