Mybatis文件加載
Configutation:
- mappedRegister 註冊接口的動態代理
- loadedResources 填充xml文件資源
- resultMaps 填充resultMap
- sqlFragments 填充sql元素
- mappedStatements 填充mappedStatment
- keyGenerators 填充KeyGenerator
org.apache.ibatis.builder.xml.XMLMapperBuilder#parse
public void parse() {
//判斷是否已經加載該配置文件
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));//處理mapper節點
configuration.addLoadedResource(resource);//將mapper文件添加到configuration.loadedResources中
bindMapperForNamespace();//註冊mapper接口
}
//處理解析失敗的ResultMap節點
parsePendingResultMaps();
//處理解析失敗的CacheRef節點
parsePendingCacheRefs();
//處理解析失敗的Sql語句節點
parsePendingStatements();
}
binging模塊
他是做什麼的?
她就是動態代理,執行sql。
什麼是mybatis的動態代理呢?
- 找到session中對應的方法執行
- 找到命名空間和方法名
- 傳遞參數
MapperRegister:mapper接口和對應的代理對象工廠的註冊中心;
MapperProxyFactory: 用於生成mapper接口動態代理的實例對象;
MapperProxy: 實現了InvocationHandler接口,他是增強mapper接口的實現;
MapperMethod: 封裝了Mapper接口中對應的方法的信息,以及對應的sql語句的信息;他是mapper接口與映射文件中sql語句的橋樑
mybatis的接口層
org.apache.ibatis.binding.MapperRegistry
//記錄了mapper接口與對應MapperProxyFactory之間的關係
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
org.apache.ibatis.binding.MapperProxyFactory
/**
*
* 用於生成mapper接口動態代理的實例對象;
* @author Lasse Voss
*/
public class MapperProxyFactory<T> {
//mapper接口的class對象
private final Class<T> mapperInterface;
//key是mapper接口中的某個方法的method對象,value是對應的MapperMethod,MapperMethod對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
//創建實現了mapper接口的動態代理對象
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
//每次調用都會創建新的MapperProxy對象
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
org.apache.ibatis.binding.MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;//記錄關聯的sqlsession對象
private final Class<T> mapperInterface;//mapper接口對應的class對象;
//key是mapper接口中的某個方法的method對象,value是對應的MapperMethod,MapperMethod對象不記錄任何狀態信息,所以它可以在多個代理對象之間共享
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
@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 (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//從緩存中獲取mapperMethod對象,如果緩存中沒有,則創建一個,並添加到緩存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
//調用execute方法執行sql
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
@UsesJava7
private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
throws Throwable {
final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
.getDeclaredConstructor(Class.class, int.class);
if (!constructor.isAccessible()) {
constructor.setAccessible(true);
}
final Class<?> declaringClass = method.getDeclaringClass();
return constructor
.newInstance(declaringClass,
MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
| MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
}
/**
* Backport of java.lang.reflect.Method#isDefault()
*/
private boolean isDefaultMethod(Method method) {
return (method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
&& method.getDeclaringClass().isInterface();
}
}
封裝方法的數據結構
public class MapperMethod {
//從configuration中獲取方法的命名空間.方法名以及SQL語句的類型
private final SqlCommand command;
//封裝mapper接口方法的相關信息(入參,返回類型);
private final MethodSignature method;
..............................
}
org.apache.ibatis.binding.MapperMethod.MethodSignature
public static class MethodSignature {
private final boolean returnsMany;//返回參數是否爲集合類型或數組
private final boolean returnsMap;//返回參數是否爲map
private final boolean returnsVoid;//返回值爲空
private final boolean returnsCursor;//返回值是否爲遊標類型
private final boolean returnsOptional;//返回值是否爲Optional
private final Class<?> returnType;//返回值類型
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;//該方法的參數解析器
......................................
}
org.apache.ibatis.binding.MapperProxy#invoke
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 (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//從緩存中獲取mapperMethod對象,如果緩存中沒有,則創建一個,並添加到緩存中
final MapperMethod mapperMethod = cachedMapperMethod(method);
//調用execute方法執行sql
return mapperMethod.execute(sqlSession, args);
}
org.apache.ibatis.binding.MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//根據sql語句類型以及接口返回的參數選擇調用不同的
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()) {//返回值爲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 = OptionalUtil.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;
}