5.2 Mapper对象间接执行
先看示例代码
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
blog = mapper.selectBlog1(1);
从第一章
的 准备示例中了解到,BlogMapper 是一个接口,并没有具体实现类,当调用 mapper.selectBlog1(1) 时,到底是谁在起作用,这里先说下答案,里面用到的是Java的两大核心法宝:反射和代理。我们沿着这个思路继续前进。
5.2.1 通过代理和反射创建Mapper接口的实现
sqlSession.getMapper 的调用链如下:
DefaultSqlSession.getMapper -> configuration.getMapper -> mapperRegistry.getMapper,最终进到 mapperRegistry.getMapper,看代码:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 首先获取 mapper的代理工厂对象,这个对象是mybatis启动时设置的,设置过程见章节`5.2.2`
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 代理模块创建类型为 type 的实现,进到 MapperProxyFactory.newInstance 方法
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
**MapperProxyFactory.newInstance **
public T newInstance(SqlSession sqlSession) {
// 创建代理对象 MapperProxy 就是 InvocationHandler 子类
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 这一步就是Java自带的代理方式调用,关于代理不会的,可以随便网上搜索下代理的使用。这一步执行完后,就算是创建了接口的代理实现类了,后续调用该接口的方法时,就会进到 InvocationHandler 这里就是 MapperProxy 的 invoke方法中,继续往里走
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
MapperProxy 的 invoke 方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
// 判断是否 Object 类
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) { // 判断是否默认方法,jdk1.8以后的功能,在接口里的方法上,增加default修饰
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
// 直接看这一步
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行方法
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
// 如果 methodCache 中没有,则放入一个新的 MapperMethod 对象,有则不放。接着看看 MapperMethod 是什么,以及它做了什么
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
new MapperMethod
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
// 这一步的创建,是为了后续执行时确定接口对应的是select|insert|update|delete
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName(); // 方法名
final Class<?> declaringClass = method.getDeclaringClass(); // 该方法所在的类型
// 从 configuration 中拿到已解析的 MappedStatement
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
// 没有已解析的语句
if (method.getAnnotation(Flush.class) != null) {
// 如果注解中有Flush
name = null;
type = SqlCommandType.FLUSH;
} else {
// 没有,抛出异常,无效语句
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
// 从已解析的语句中,拿到id和type(select|insert|update|delete)
name = ms.getId();
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
// 未知类型,抛异常
throw new BindingException("Unknown execution method for: " + name);
}
}
}
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
// 类型全限定名+方法名,代表语句id,与XXXMapper.xml中编写的sql语句id,一一对应
String statementId = mapperInterface.getName() + "." + methodName;
// 已经解析过这个语句,直接取出来就行
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
// 没有解析过该语句,并且本身没有再继承其他接口,直接返回 null
return null;
}
// 如果还有继承其他接口,则遍历它们
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
// 递归
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
执行方法
// 最终执行走到这个方法,因为前面对insert|select已经做了细致的铺垫,所以,这一段代码就非常容易理解了
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
// insert 直接看章节`5.1.2`
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
// 参考 insert|select 读者自行阅读源码扩展
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
// 参考 insert|select 读者自行阅读源码扩展
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
// 自带 ResultHandler 的方式,将在`第六章`和`第七章`中讲
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
// 涉及多个返回值的,一般指存储过程的执行
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
// 当对应的mapper接口方法,通过 @MapKey 注解标注,并且返回值是Map的时候,一般用在多表连接查询,又不想创建新的 domain 对象时,可以这样组装成 Map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
// 返回类型为 Cursor
result = executeForCursor(sqlSession, args);
} else { // 直接走到这里
// 参数转换
Object param = method.convertArgsToSqlCommandParam(args);
// 这里又到了真正执行的地方,看章节`5.1.1`
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
5.2.2 Mapper代理工厂的设置
调用链:new SqlSessionFactoryBuilder().build(inputStream) -> XMLConfigBuilder.parse() -> XMLConfigBuilder.mapperElement -> configuration.addMapper(mapperInterface) -> mapperRegistry.addMapper
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) { // 必须是接口类型
if (hasMapper(type)) { // 判断重复
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// knownMappers 是一个HashMapp,往里放入 MapperProxyFactory 对象,由于这一步操作是从build()进来的,mybatis只会build一次,所以 MapperProxyFactory 对象也只会有这一个,后续所有的 mapper 接口的实现类都由它来代理实现
knownMappers.put(type, new MapperProxyFactory<>(type));
// 后面这几行代码与addMapper的主流逻辑已经不相关了,这里主要是针对Mybatis的注解形式,后续章节会单独讲注解形式的源码
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}