MyBatisMyBatis源码解读(3)——MapperMethod

在眼下两篇的MyBatis源码解读中,大家联合跟踪到了MapperProxy,知道了即便是应用了动态代理技术驱动大家能一贯行使接口方法。为加固深化动态代理,我们不妨再来记念五回何为动态代理。

自身相信在初学MyBatis的时候大致每种人都会暴发一个疑问,为何明明是XXXDao接口,我从不用此外代码完成那些接口,但却能直接行使这几个接口的不二法门。现在清楚了,动态代理。大家来写一个demo小程序来看看。

首先是一个Test.java的接口,只有一个say方法。

 1 package day_16_proxy;
 2 
 3 /**
 4  * @author 余林丰
 5  *
 6  * 2016年11月16日
 7  */
 8 public interface Test {
 9     void say();
10 }

咱俩前日设想MyBatis那样不用完毕它而是一贯调用。

test.say();

当然不能够一直实例化一个接口,此时就要求生成一个代理类TestProxy.java,这么些类需兑现InvocationHandler接口。

 1 package day_16_proxy;
 2 
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 /**
 7  * @author 余林丰
 8  *
 9  * 2016年11月16日
10  */
11 public class TestProxy implements InvocationHandler {
12 
13     /* (non-Javadoc)
14      * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
15      */
16     @Override
17     public Object invoke(Object proxy, Method method, Object[] args)
18             throws Throwable {
19         if (method.getName().equals("say")){
20             System.out.println("hello world");
21         }
22         return null;
23     }
24 
25 }

亟待落成invoke方法,意思就是“调用”的意趣(当然大家想要调用接口中的方法时并不会显得调用invoke)。大家从第19行看到,当调用的点子是say时,输出“hello
world”。有了那个TestProxy.java代理类过后,我们再来客户端代码中测试。

 1 package day_16_proxy;
 2 
 3 import java.lang.reflect.Proxy;
 4 
 5 /**
 6  * @author 余林丰
 7  *
 8  * 2016年11月16日
 9  */
10 public class Client {
11 
12     public static void main(String[] args){
13         Test test = (Test)Proxy.newProxyInstance(Test.class.getClassLoader(), new Class<?>[]{Test.class}, new TestProxy());
14         test.say();
15     }
16 }

在第14行代码中,大家曾经能够直接调用Test接口中的say方法了,原因就在于我们通过Proxy.newProxyInstance方法生成了一个代理类实例即TestProxy。

回去我们的MyBatis源码,在上一节中大家精通了一个Dao接口实际上是经过MapperProxyFactory生成了一个MapperProxy代理类。

1 //org.apache.ibatis.binding.MapperProxyFactory
2 protected T newInstance(MapperProxy<T> mapperProxy) {
3   return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
4 }

第3行代码是还是不是和在起来的事例中客户端测试代码如出一辙?生成代理类,那么些代理类就是MapperProxy。清楚了MyBatis是何等社团出代理类的百川归海解决了第三个难点——一个接口怎么能平素调用其方法。

方今抛出第三个难题——接口中每一个具体的章程是怎么着形成一一落成代理的吗?我们再来看看MapperProxy类。这一次大家先看MapperProxy类完毕的InvocationHanlder.invoke方法。

 1 //org.apache.ibatis.binding.MapperProxy
 2 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 3   if (Object.class.equals(method.getDeclaringClass())) {
 4     try {
 5       return method.invoke(this, args);
 6     } catch (Throwable t) {
 7       throw ExceptionUtil.unwrapThrowable(t);
 8     }
 9   }
10   final MapperMethod mapperMethod = cachedMapperMethod(method);
11   return mapperMethod.execute(sqlSession, args);
12 }

第3行做一个判定,判断是还是不是是一个类,如果是一个类,那么久直接传送方式和参数调用即可。但大家通晓此刻是一个接口(也足以协调落成接口,旧版本家常便饭那样做)。如若不是一个类的话,就会创造一个MapperMethod方法。见名思意:好像就是以此类在实践我们所调用的逐个接口方法。最终回到的是MapperMethod.execute方法。暂时不予理会MapperProxy类中的cachedMapperMethod方法。

来探望MapperMethod类,那个MapperMethod类就充裕了哟,可以说它是统管所有和数据库打交道的主意(当然包罗起来也唯有insert、delete、update、select五个措施)。所以,不管你的dao层有微微方法,归咎起来的sql语句都有且仅有唯有insert、delete、update、select,可以预期在MapperMethod的execute方法中首先判断是何种sql语句。

//org.apache.ibatis.binding.MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
    switch (command.getType()) {
        case INSERT: {……}
        case UPDATE: {……}
        case DELETE: {……}
        case SELECT: {……}
        case FLUSH: {……}
    }
}

那是MepperMethod.execute方法的删除,我们得以看到真的在execute方法内部首先判断是何种sql语句。(注意:在翻阅那部分源代码时,大家的主线是MyBatis是何等成立出一个代理类,以及落实其艺术的,而暂时忽略其中的细节)

我们选拔周边的”SELECT”sql语句来举行解读,而在”SELECT”语句中又会统筹到较多的细节难点:

 1 //org.apache.ibatis.binding
 2 case SELECT:
 3         if (method.returnsVoid() && method.hasResultHandler()) {
 4           executeWithResultHandler(sqlSession, args);
 5           result = null;
 6         } else if (method.returnsMany()) {
 7           result = executeForMany(sqlSession, args);
 8         } else if (method.returnsMap()) {
 9           result = executeForMap(sqlSession, args);
10         } else if (method.returnsCursor()) {
11           result = executeForCursor(sqlSession, args);
12         } else {
13           Object param = method.convertArgsToSqlCommandParam(args);
14           result = sqlSession.selectOne(command.getName(), param);
15         }
16         break;

咱俩选择第7行中的executeForMany中的方法来解读试试看。

 1 //org.apache.ibatis.binding.MapperMethod
 2 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
 3   List<E> result;
 4   Object param = method.convertArgsToSqlCommandParam(args);
 5   if (method.hasRowBounds()) {
 6     RowBounds rowBounds = method.extractRowBounds(args);
 7     result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
 8   } else {
 9     result = sqlSession.<E>selectList(command.getName(), param);
10   }
11   // issue #510 Collections & arrays support
12   if (!method.getReturnType().isAssignableFrom(result.getClass())) {
13     if (method.getReturnType().isArray()) {
14       return convertToArray(result);
15     } else {
16       return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
17     }
18   }
19   return result;
20 }

第7行和第9行代码就是我们确实实施sql语句的地方,原来兜兜转转它又赶回了sqlSession的章程中。在下一节,我们再重新归来紧要的SqlSession中。

 

网站地图xml地图