系列文章目录
文章目录
- 系列文章目录
- 一、从getMapper说起
- 二、SqlCommand:SQL解析+父子接口
- 三、MethodSignature方法源码
-
- [3.1. MapKey注解](#3.1. MapKey注解)
- 四、execute()
一、从getMapper说起
UserMapper mapper = sqlSession.getMapper(UserMapper.class);当调用 Mapper 接口的方法时,动态代理会拦截这个调用,并根据方法签名和配置,将请求交给 MyBatis 的 SQL 执行器(SqlSession)来完成数据库操作,
Invoker 是 MyBatis 内部用来封装方法调用逻辑的类,它负责:
解析方法参数
根据方法名找到对应的 SQL
执行 SQL 并处理结果
返回最终结果(如 List、Map、实体类等)
1.检查这个 method 是不是定义在 Object 类中的方法(比如 toString(), equals() 等)。
如果是,就直接调用该方法(因为这些方法不需要 MyBatis 处理)。
this 表示当前代理对象本身(虽然一般不会调用 Object 的方法,但这是为了防止错误处理)。
2.cachedInvoker(method):从缓存中获取一个 Invoker 对象,用于执行该方法。
c
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
继续追踪这行源码:cachedInvoker(method).invoke(proxy, method, args, sqlSession);先看cachedInvoker这个代码里面做了什么。MapperMethodInvoker 有2种, 一种是PlainMethodInvoker, 一种DefaultMethodInvoker,有什么区别呢?
DefaultMethodInvoker是指当前代理对象(接口,比如UserMapper),执行的是不是里面的默认方法,是默认方法,返回DefaultMethodInvoker, 不是默认方法,返回PlainMethodInvoker
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
// 方法不是接口中的默认方法,PlainMethodInvoker的底层会调用MapperMethod的execute方法,从而执行方法对应的SQL
if (!m.isDefault()) {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
// 方法如果是接口中的默认方法,那么方法已经有逻辑了,DefaultMethodInvoker是专门针对默认方法的MapperMethodInvoker,底层会利用反射执行默认方法的逻辑,不会执行SQL
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
}
return new DefaultMethodInvoker(getMethodHandleJava9(method));
接下来看一下PlainMethodInvoker里面的MapperMethod,代码如下:
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
二、SqlCommand:SQL解析+父子接口
bash
SqlCommand:
解析该方法对应的 SQL 是 SELECT、INSERT、UPDATE 还是 DELETE。
获取该方法对应的 SQL 语句(从 XML 或注解中读取)。
确定 SQL 的 ID(即 namespace.methodName 的形式)。
例如:如果 UserMapper.selectUserById 方法对应 XML 中的 <select id="selectUserById"></select id="selectUserById">``,那么 SqlCommand` 就会记录这个信息。
c
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
final String methodName = method.getName();
final Class<?> declaringClass = method.getDeclaringClass();
// 找到方法对应的MappedStatement,根据接口名,方法名,找到MappedStatement对象
// 1、接口可以继承,子接口的代理对象可以调用父接口的方法
// 2、一个接口可以继承多个接口,遍历每个父接口找到当前调用方法对应的MappedStatement对象
MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration);
省略.....主要看如何得到ms,请看2.1
返回ms以后,就可以得到sql的很多信息,比如当前执行的命令是什么,select/update/delete/xx, 不管是解析xml标签出来的,还是解析方法上面注解出来的
2.1、父子接口MappedStatement源码解析
public interface UserMapper{
public UserInfo getUserInfo(int userId);
}
public interface AdminMapper extends UserMapper{}
Main:
AdminMapper mapper = sqlSession.getMapper(AdminMapper.class);
这种使用就会涉及到父子接口
c
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass,
Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
//这个if语句比较容易理解,直接从configuration里面拿
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
}
// 什么时候不相等呢?接口可以继承,子接口的代理对象可以调用父接口的方法
//请看2.1 开始部分代码示例
if (mapperInterface.equals(declaringClass)) {
return null;
}
// 一个接口可以继承多个接口,遍历每个父接口找到当前调用方法对应的MappedStatement对象
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = resolveMappedStatement(superInterface, methodName, declaringClass, configuration);
if (ms != null) {
return ms;
......
三、MethodSignature方法源码
用于解析接口方法的签名,主要作用是:解析并保存当前方法的返回类型,参数信息,是否为集合,是否使用@MapKey等元数据信息,以便后续在sql映射中使用这些信息进行参数绑定和结果映射
c
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
// 先获取方法的返回类型
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
// 根据返回类型
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method); // 获取方法上@MapKey指定的值
this.returnsMap = this.mapKey != null; // 使用了@MapKey
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); // // 获取方法中RowBounds类型参数的参数Index
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); // 获取方法中ResultHandler类型参数的参数Index
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
3.1. MapKey注解
平时比较少用
c
@Select("select * from user_info;")
@MapKey("name")
//MapKey注解里面name的值是UserInfo类中的某个属性
//表示将查询结果封装到Map中,Map的key是UserInfo类中的name属性,value是UserInfo对象
public Map<String, UserInfo> selectAllUser();
四、execute()
前面说了sqlcommand,MethodSignature等等,有了这两个,最终会执行下面这个方法PlainMethodInvoker.execute()
c
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 SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
// 方法中有ResultHandler类型的参数,那就用该参数对应的ResultHandler对象来处理SQL的返回结果
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 {
// param要么是一个对象,要么是ParamMap, key就是参数的名字,value就是参数值
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);
}
}