Previous:
Mapper dynamic proxy for mybatis source code analysis
https://my.oschina.net/u/657390/blog/748646
The relationship between MapperMethod and MapperProxy, MapperProxyFactory, MapperRegistry, Configuration
When analyzing the mapper dynamic proxy, it can be seen that the final execution is mapperMethod.execute
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//当执行的方法是继承自Object时执行this里的相应方法
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);
}
//最终执行的是mapperMethod.execute
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
Then analyze mapperMethod.execute()
First look at cachedMapperMethod(method) in MapperProxy
/**
* methodCache中已经存在传入的参数method
* 则从methodCache中取MapperMethod,
* 否则根据method生成MapperMethod实例并存储到methodCache中
*
* @param method
* @return
*/
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
MapperProxy中methodCache
private final Map<Method, MapperMethod> methodCache;
Trace the source of methodCache
//MapperProxy构造方法
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
MapperProxyFactory中
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
It can be seen from the source code of MapperProxyFactory that methodCache is initially an empty ConcurrentHashMap,
It can be seen from the cachedMapperMethod(method) in MapperProxy
The incoming parameter method already exists in the methodCache, then the MapperMethod is taken from the methodCache, otherwise the MapperMethod instance is generated according to the method and stored in the methodCache.
Then look at the source code of MapperMethod
Construction method
/**
* @param mapperInterface 接口
* @param method 调用的方法
* @param config 配置
*/
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
SqlCommand and MethodSignature instances are created
command type
public enum SqlCommandType {
UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;
}
The most important method in 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);
}
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;
}
First call the method in MethodSignature
public Object convertArgsToSqlCommandParam(Object[] args)
Process the parameters. Then,
if command type is
INSERT, UPDATE, DELETE
Then directly call the corresponding method of sqlSession
if command type is
SELECT
Then call the corresponding query method in sqlSession according to the returned result.
So far, the process of MapperMethod from creation to execution has been roughly analyzed.