接着上面的一继续往下看:
SQLSession的执行过程,有了SQLSessionFactory对象就可以轻易的获取SQLSession。不过SQLSession也是一个接口。
在之前写的文章中,我们看到这段代码:
UserDao mapper=sqlSession.getMapper(UserDao.class);
从这句代码我们看出,我们只是给了一个Mapper接口便能获取到想要的对象了;不过在这里,我们获得的是代理对象,让我们看一下它到底怎么一步步获取到的:
首先我们看一下这个getMapper的方法,由于SQLSession是一个接口所以我们去它的实现类DefaultSqlSession中查看这个方法:
@Override
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
在这里我们能够看到,它运行到了configuration对象的getMapper方法来获取对应的接口对象的。继续跟进:
我看进入到Configuration的类中了,看一下方法:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
它竟然在这里调用了映射器的注册器mapperRegistry的getMapper方法来获取对应接口对象的,那我们来看一下这个mapperRegistry注册器:
@SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
首先通过 if (mapperProxyFactory == null)方法判断是否注册了一个Mapper,如果没有则会抛出异常;如果有,就会调用mapperProxyFactory对象来生成一个代理机制;继续跟进:
我们进入MapperProxyFactory类
@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);
}
看到这里是不是感觉很熟悉了,protected T newInstance(MapperProxy<T> mapperProxy)这个方法生成的是一个动态代理对象,因此mapper映射是通过动态代理来实现的。
我们来看一下代理方法:
@Override
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);
}
这个方法是被放在MapperProxy类中的,它首先判断了一下Mapper是否是一个类,这里肯定是一个接口;注意这里的代理是jdk动态代理;是不是问为什么会这么说,那我们来看一下这段代码:
public class MapperProxy<T> implements InvocationHandler, Serializable
看到了什么,不错是InvocationHandler,现在知道为什么他是jdk动态代理了吧!由于Mapper是一个接口,所以判断是false,会走
final MapperMethod mapperMethod = cachedMapperMethod(method);
生成一个MapperMethod对象,最后执行它的execute方法,而且传递的只有SQLSession和当前运行参数。由于这个方法比较多,不重要的点就不截取了,
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
}
这里调用的 executeForMap(sqlSession, args);方法,那我们来看一下这个方法:
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
Map<K, V> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
} else {
result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
}
return result;
}
到这里,就不在继续跟进了,我们停留在这个executeForMap这个方法的实现即可,它通过命令模式运行;通过源码我们能够知道它最后就是通过SQLSession对象去运行对象的sql的,其他的增删改这是类似于这样的操作;
这里我们就应该明白,mapper的xml配置文件命名空间对应的就是这个接口的全类名,而方法就是那条sql的id,这样mybatis就可以根据全类名和方法名,将其和代理绑定在一起;
这一节,我们看到了SQLSession大体的工作流程,就是初始化配置文件的sql,并向其中传递参数,执行sql语句并获取返回值;
那么它是怎么执行的,下一篇再细说;