注意:若不理解代理模式和jdk動態(tài)代理請先了解再接下去閱讀纳决,效果更好订晌。
1布近、首先舉個簡單例子來說明
1.1先創(chuàng)建Mapper
比作mybatis的xxxMapper,就是Mapper.xml對應的接口類餐屎。
public interface Mapper {
String call();
}
1.2創(chuàng)建MapperXml
MapperXml相當于mybatis執(zhí)行數(shù)據(jù)庫操作的代理類徐块,每一個方法對應一個MapperXml此洲。
public class MapperXml {
public String call() {
return "xxxx";
}
}
1.3創(chuàng)建代理的Handler類
jdk代理的實現(xiàn)就是實現(xiàn)InvocationHandler接口畅厢,實現(xiàn)invoke方法,當調(diào)用Mapper的方法時元莫,實際是調(diào)用了MapperHandler的invoke方法赖阻。而mybatis的Mapper接口不能有重載方法名,就是因為在初始化時方法名作為key值踱蠢,不能存在相同的方法名火欧。
public class MapperHandler implements InvocationHandler {
//根據(jù)方法名保存一份對應的操作類。
private static final Map<String, MapperXml> mapperXmlMap = new HashMap<>();
static {
//初始化默認
mapperXmlMap.put("call", new MapperXml());
}
public void addMapperProxy(String method, MapperXml mapperXml) {
mapperXmlMap.put(method, mapperXml);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//根據(jù)方法名取到對應的實現(xiàn)類,然后調(diào)用對應的方法苇侵。
return mapperXmlMap.get(method.getName()).call();
}
}
1.4創(chuàng)建SqlSession
此SqlSession相當于mybatis的SqlSession赶盔,根據(jù)Mapper的類型獲取到相應的代理類。
public class SqlSession {
public static <T> T getMapper(Class<T> clazz) {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new MapperHandler());
}
}
1.5測試例子
public static void main(String[] args) {
Mapper test = SqlSession.getMapper(Mapper.class);
System.out.println(test.call());
//輸出xxxx
}
總結:mybatis調(diào)用mapper接口其實就是返回的代理類榆浓,由代理類去執(zhí)行數(shù)據(jù)庫操作于未。
2、mybatis對應的源碼解釋
當執(zhí)行自己的Mapper接口時哀军,實際調(diào)用的是MapperProxy的invoke方法,而MapperProxy相當于上面提到的MapperHandler打却,MapperMethod相當于MapperXml杉适,下面是它的主要方法:
@Override
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 (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//根據(jù)方法名找到對應的操作類
final MapperMethod mapperMethod = cachedMapperMethod(method);
//執(zhí)行操作
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
//看map是否存在這個類,若沒有則新建
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
最后看看MapperMethod的execute方法柳击,根據(jù)sql類型執(zhí)行不同操作猿推,最后都會調(diào)用sqlSession的方法,對數(shù)據(jù)庫操作捌肴。若想了解更多可以看mybatis下的org.apache.ibatis.binding包蹬叭,MapperProxyFactory主要創(chuàng)建Mapper的代理類,MapperRegistry主要緩存相應的代理對象状知,并提供查找添加等方法秽五。
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;
}