java架构师学习路线-揭示Mapper类背后的执行逻辑(下)
作者:互联网
图灵学院 java架构师学习路线
NO.2|通过Mapper代理对象调用方法
那么在调用mapper.findUser(user)又会发生什么呢,相信你心里也有了一些猜想。首先,这里的mapper是一个代理对象,所以在通过代理对象调用方法的时候一定会调用MapperProxy的invoke方法,我们大胆猜测,mybatis在invoke方法中做了处理,调用了sqlSession的方法,从而可以进行SQL的执行。具体是不是这样呢,还要继续向下探究。
可以肯定的是通过mapper代理对象调用方法的时候,一定会走MapperProxy的invoke方法,所以这里是执行的入口,探究之旅也从这里开始。
可以看见在MapperProxy的invoke方法中,调用了MapperMethod的execute(sqlSession, args)方法。
public class MapperProxy<T> implements InvocationHandler, Serializable:
private final Map<Method, MapperMethod> methodCache;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 执行sqlsession的方法
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
//判断methodCache中以method为key有没有值存在, 如果没有创建一个MapperMethod对象放进去
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
MapperMethod是整个代理机制的核心类,我们一直心心念念的sqlSession的操作就是在这个类中进行了封装。所以有必要先来认识一下这个类以及它的成员变量,方便我们后续的理解。
它的成员变量有两个,一个是SqlCommand【注2】命令类型,还有一个是MethodSignature【注3】方法签名。
public class MapperMethod {
private final SqlCommand command;
private final MethodSignature method;
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
}
【注2】:SqlCommand命令类型,SqlCommand的构造方法中会根据mapperInterface.getName() + "." + method.getName()生成statementId,然后根据statementId从configuration中取出对应的MappedStatement(每个sql语句会被封装成一个MappedStatement),进而通过ms.getSqlCommandType()获得此次SQL命令的类型—增、删、改、查。
【注3】:MethodSignature方法签名,里面定义了关于方法的相关信息,具体是什么可以看下面的注释部分。
public MethodSignature(Configuration configuration, Method method) throws BindingException {
this.returnType = method.getReturnType(); // 返回值类型
this.returnsVoid = void.class.equals(this.returnType); // 返回值是否为void
this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); // 是否返回多个结果
this.mapKey = getMapKey(method); // method是否使用了mapKey注解
this.returnsMap = (this.mapKey != null); // 返回值是否是Map类型
this.hasNamedParameters = hasNamedParams(method); // 参数是否自定义了别名
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); // 是否使用mybatis的物理分页
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); // 是否有resutlHandler
this.params = Collections.unmodifiableSortedMap(getParams(method, this.hasNamedParameters)); // 请求参数解析
}
认识了MapperMethod这个类之后,就要探究它的execute方法的执行逻辑了。在execute方法中,会先判断SQL语句的命令类型—增、删、改、查。选择不同的执行逻辑。
而增删改查执行逻辑大致相同,先是调用method.convertArgsToSqlCommandParam(args)进行参数转换(在介绍参数处理的整个流程的时候会详细介绍)然后就是调用sqlSession对应的增删改方法。
而当进行查询操作的时候,可以看到根据返回类型的不同,会执行不同的方法,然而殊途同归,最终都会先调用convertArgsToSqlCommandParam进行参数转化,然后调用sqlSession的不同的查询api。
我们这里仅以需要返回多个结果,也就是返回参数是List的情况为例,进行探究。当返回结果为List时,会调用executeForMany(sqlSession, args)方法。
public class MapperMethod:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
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);
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;
}
可以清楚的看见在executeForMany方法中, 进行了参数转换后,调用了sqlSession.selectList()得到结果,最后根据method定义的返回类型进行数据封装。终于,我们在Mapper代理对象执行方法的过程中,成功的找到了sqlSession,可以用它与数据库交互了。
public class MapperMethod:
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
} else {
result = sqlSession.<E>selectList(command.getName(), param);
}
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
NO.3|小结
mybatis在 session.getMapper(UserMapper.class); 的时候获取代理对象工厂,使用代理对象工厂创建MapperProxy,并利用MapperProxy创建JDK动态代理对象,然后返回代理对象。
mybatis在 mapper.findUser(user); 执行的时候,其实是用代理对象来执行方法,在执行的过程中,会调用MapperProxy的invoke方法,invoke方法中会通过MapperMethod调用sqlSession的方法操作SQL。
截止到这里,当调用mapper.findUser(user)时mybatis在干什么?相信大家已经清楚了。
尽管Java架构师学习路线已经分享给大家,但有多少人能认真的去践行,这个就难说了。互联网寒冬已经到来,作为程序员,更应在此时提高自己,有着更高远的追求。
篇幅有限,如果需要更详细的java架构师学习路线资料可加博主qq:1993712276,或者去图灵官网查看
标签:Mapper,Object,java,args,sqlSession,result,架构师,MapperMethod,method 来源: https://www.cnblogs.com/tulingxueyuan/p/13598552.html