学习源码过程中随手记录的笔记,仅供参考,有问题欢迎指出交流
可能比较枯燥,耐点心,但是弄懂了,必能知其然而知其所以然
学习源码建议亲手debug调试
使用的源码版本
mybatis版本3.5.3
spring版本5.2.0
前面分析完dao接口会被创建动态代理加入到spring容器中,可以供我们使用
现在看下当执行dao接口里的方法时,是如何找到对应的xml并执行sql的
当调用mapper接口中的方法时,由于这个接口已经被动态代理加强过,那么会走到MapperProxy的invoke方法
MapperProxy#invoke()
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 (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
/**
* 真正的进行调用,做了二个事情
* 第一步:把我们的方法对象封装成一个MapperMethod对象(带有缓存作用的)
*/
final MapperMethod mapperMethod = cachedMapperMethod(method);
//执行目标方法
return mapperMethod.execute(sqlSession, args);
}
进入
cachedMapperMethod(method);
private MapperMethod cachedMapperMethod(Method method) {
/**
* 相当于这句代码.jdk8的新写法
* if(methodCache.get(method)==null){
* methodCache.put(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()))
* }
*/
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
进入
new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
/**
* 创建我们的SqlCommand对象
*/
this.command = new SqlCommand(config, mapperInterface, method);
/**
* 创建我们的方法签名对象
*/
this.method = new MethodSignature(config, mapperInterface, method);
}
进入
new SqlCommand(config, mapperInterface, method);
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
//获取我们的方法的名称 selectById
final String methodName = method.getName();
//方法所在接口的类型 UserMapper
final Class<?> declaringClass = method.getDeclaringClass();
/**
* 根据接口,方法名称解析出我们对应的mapperStatment对象
*/
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
configuration);
if (ms == null) {
if (method.getAnnotation(Flush.class) != null) {
name = null;
type = SqlCommandType.FLUSH;
} else {
throw new BindingException("Invalid bound statement (not found): "
+ mapperInterface.getName() + "." + methodName);
}
} else {
//把我们的mappedStatmentID(com.cheng.mapper.UserMapper.selectById)
name = ms.getId();
//sql操作的类型(比如insert|delete|update|select)
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
进入
resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration)
还记得前面解析xml文章提到过两个缓存map,其中一个就是将sql封装成mappedStatement放入缓存中,这边获取之前解析的xml文件,在这去拿了
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
//获取我们的sql对应的statmentId(com.cheng.mapper.UserMapper + selectById)
String statementId = mapperInterface.getName() + "." + methodName;
//根据我们的statmentId判断我们的主配置类是否包含 了我们的mapperStatment对象
if (configuration.hasStatement(statementId)) {
//存在通过key获取对应的mapperStatment对象返回
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
/**
* 获取我们mapper接口的父类接口
*/
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
//判断方法所在的类是否实现了superInterface
if (declaringClass.isAssignableFrom(superInterface)) {
//解析我们父类的MappedStatment对象
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
根据接口里的statementId (接口全限定名+方法名 - 对应xml的nameSpace + selectid)
去mybatis全局配置configuration中找对应的mappedStatements
这里就是通过接口dao的调用最终怎么找到对应的xml的
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5CN4czY0kjYmRDNzczM3kTM0kjNzETZzUTNkFmZjRGOj9CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
最终会封装成MapperMethod对象,里面包含调用方法名称,执行的语句,返回值,参数值…
通过dao方法的调用封装了对应的xml执行语句,下面再看看如何执行的
进入
MapperProxy#invoke# mapperMethod.execute(sqlSession, args);
根据MapperMehod获取对应的操作类型通过SqlSessionTemplate调用即可
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
/**
* 判断我们执行sql命令的类型
*/
switch (command.getType()) {
//insert操作
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
//update操作
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
//delete操作
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
//select操作
case SELECT:
//返回值为空
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//返回值是一个List
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
//返回值是一个map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
//返回游标
result = executeForCursor(sqlSession, args);
} else {
//查询返回单个
/**
* 解析我们的参数
*/
Object param = method.convertArgsToSqlCommandParam(args);
/**
* 通过调用sqlSessionTemplate来执行我们的sql
* 第一步:获取我们的statmentName(com.cheng.mapper.UserMapper.selectById)
* 然后我们就需要重点研究下SqlSessionTemplate是怎么来的?
* 在mybatis和spring整合的时候,我们偷天换日了我们mapper接口包下的所有的
* beandefinition改成了MapperFactoryBean类型的
* MapperFactoryBean<T> extends SqlSessionDaoSupport的类实现了SqlSessionDaoSupport
* 那么就会调用他的setXXX方法为我们的sqlSessionTemplate赋值
*
*/
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;
}
到这整个spring整合mybatis源码已经剖析完毕了
只要跟着源码多走几遍基本能弄懂一点吧,不得不说spring还是很强大,集成三方处理的很好
如果觉得还算凑合,帮我点个赞,谢谢