这是我参与8月更文挑战的第24天,活动详情查看:8月更文挑战
时序图
sequenceDiagram
participant A as Client
participant F as PurchaseMapper
participant B as MapperProxy
participant E as MapperMethod
participant C as SqlCommand
participant D as MethodSignature
participant G as ParamNameResolver
A -->> F : findByCondition
A ->> B : invoke
B ->> E : new
E ->> C : new
C -->> E : SqlCommand
E ->> D : new
D ->> G : new
G -->> D : ParamNameResolver
D -->> E : MethodSignature
E -->> B : MapperMethod
B ->> E : execute
E -->> A : ArrayList<Purchase>
详细步骤
Client
QueryCondition condition = new QueryCondition();
condition.setId(1);
condition.setCategory(1);
// 调用mapper方法,执行查询
List<Purchase> purchaseList = mapper.findByCondition(condition);
PurchaseMapper#findByCondition
/**
* 根据条件查询
*/
List<Purchase> findByCondition(QueryCondition condition);
MapperProxy#invoke
/**
* 代理方法
* @param proxy 代理对象
* @param method 代理方法
* @param args 代理方法的参数
* @return 方法执行结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) { // 继承自Object的方法
// 直接执行原有方法
return method.invoke(this, args);
} else if (method.isDefault()) { // 默认方法
// 执行默认方法
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
/**
* 1. 以method为key,缓存当前方法对应的MapperMethod
* 2. 如果存在method key,则返回对应的value
*/
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 调用MapperMethod中的execute方法,执行操作,并获取结果并返回
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
// 有key就返回对应的MapperMethod,没有则创建一个新的MapperMethod
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
MapperMethod
/**
* MapperMethod的构造方法
*
* @param mapperInterface 映射接口
* @param method 映射接口中的具体方法
* @param config 配置信息Configuration
*/
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
SqlCommand
- MappedStatement的映射、SqlCommandType的获取,见Mybatis源码之mappers标签解析
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
// 方法名称
final String methodName = method.getName();
// 方法所在的类,可能是mapperInterface,也可能是mapperInterface的子类
final Class<?> declaringClass = method.getDeclaringClass();
// 获取MappedStatement
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 {
// 获取MappedStatement的ID
name = ms.getId();
// 获取SqlCommandType
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
/**
* 找出指定接口指定方法对应的MappedStatement对象
* @param mapperInterface 映射接口
* @param methodName 映射接口中具体操作方法名
* @param declaringClass 操作方法所在的类。一般是映射接口本身,也可能是映射接口的子类
* @param configuration 配置信息
* @return MappedStatement对象,MappedStatement完整对应了一条数据库 操作语句
*/
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
// 数据库操作语句的编号是:接口名.方法名
String statementId = mapperInterface.getName() + "." + methodName;
// configuration保存了解析后的所有操作语句,去查找该语句
if (configuration.hasStatement(statementId)) {
// 从configuration中找到了对应的语句,返回
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
// 说明递归调用已经到终点,但是仍然没有找到匹配的结果
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;
}
MethodSignature
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);
// 处理返回值是MAP的情况
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
// 解析参数中RowBounds对象的位置
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
// 解析参数中ResultHandler对象的位置
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
// 参数名称解析
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
ParamNameResolver
- 处理方法的参数名称
/**
* 参数名解析器的构造方法
*
* @param config 配置信息
* @param method 要被分析的方法
*/
public ParamNameResolver(Configuration config, Method method) {
// 获取方法参数类型列表
final Class<?>[] paramTypes = method.getParameterTypes();
// 准备存储所有的参数的注解,是二维数组,分别存储参数顺序与参数注解(0,Param),(1,Param)
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
// 循环处理各个参数
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// 跳过RowBounds与ResultHandler参数
if (isSpecialParameter(paramTypes[paramIndex])) {
// skip special parameters
// 跳过特别参数
continue;
}
// 参数名称
String name = null;
for (Annotation annotation : paramAnnotations[paramIndex]) {
// 找出参数的注解@Param
if (annotation instanceof Param) {
// 如果注解是 Param
hasParamAnnotation = true;
// 那就以Param中的值作为参数名
name = ((Param) annotation).value();
break;
}
}
if (name == null) {
// 如果没有@Param注解,并且在配置文件中配置了useActualParamName=true,则使用参数实际的名称
// 但为了使用该特性,必须采用Java8编译,并且编译时需要加上-parameters选项,否则实际参数名称形如arg0
// @Param was not specified.
if (config.isUseActualParamName()) {
name = getActualParamName(method, paramIndex);
}
if (name == null) {
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
// 参数名称取不到,则按照参数的index命名
name = String.valueOf(map.size());
}
}
// 参数顺序:参数名称
map.put(paramIndex, name);
}
// 锁定map,不可修改
names = Collections.unmodifiableSortedMap(map);
}
MapperMethod#execute
/**
* 执行映射接口中的方法
*
* MapperMethod类将一个数据库操作语句和一个 Java方法绑定在了一起:它的MethodSignature属性保存了这个方法的详细信息;
* 它的 SqlCommand属性持有这个方法对应的 SQL语句。因而只要调用 MapperMethod对象的 execute方法,就可以触发具体的数据库操作,于是数据库操作就被转化为了方法
*
* @param sqlSession sqlSession接口的实例,通过它可以进行数据库的操作
* @param args 执行接口方法时传入的参数
* @return
*/
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) { // 根据SQL语句类型,执行不同的操作
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()) { // 返回返回为void,且有结果处理器
// 使用结果处理器执行查询
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) { // 多条结果查询
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);
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()) {
// 查询结果为null,但返回类型为基本类型。因此返回变量无法接收查询结果,抛出异常。
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
// 返回执行结果
return result;
}
以上便是Mybatis中Mapper方法执行流程。