MyBATIS插件原理第二篇 Mapper运行原理
2015-12-15 13:49
302 查看
我们目前在MyBATIS中,我们知道MyBATIS的Mapper是一个接口,而不是一个实体类。在Java中接口是没有办法运行的。那么它是怎么运行的呢?
有了第一篇的基础,我们可以大胆的想象——它是通过动态代理运行,没有错真实的情况就是这样的。
让我们看看mybatis是怎么实现这个动态代理的:
于是让我们看看这个execute方法的源码:
好这里我们看到,MapperMethod 类采用命令模式运行,然后根据上下文跳转可能跳转到许多方法中取,我们不需要全部明白,我们可以看到里面的executeForMany方法,再看看它的实现,实际上它最后就是通过sqlSession对象去运行对象的SQL而已。
至此,相信大家已经了解了MyBATIS为什么只用mappper接口便能够运行sql,因为mapperd的xml文件的命名空间对应的便是这个接口全路径,那么它根据全路径和方法名,便能够绑定起来,通过动态代理技术,让这个接口跑起来。
有了第一篇的基础,我们可以大胆的想象——它是通过动态代理运行,没有错真实的情况就是这样的。
让我们看看mybatis是怎么实现这个动态代理的:
/** * Copyright 2009-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.ibatis.binding; import java.io.Serializable; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Map; import org.apache.ibatis.reflection.ExceptionUtil; import org.apache.ibatis.session.SqlSession; /** * @author Clinton Begin * @author Eduardo Macarron */ public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; 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 { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } 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; } }这便是mybaitis处理对象的方法,我们可以看到invoke方法。我们知道一旦mapper是一个代理对象,那么它就会运行到invoke方法里面,invoke首先判断是否一个类,显然这里mapper是一个接口,不是类所以判定失败。那么跟着就会生成MapperMethod对象,它是通过cachedMapperMethod方法对其初始化的,然后执行execute方法,把sqlSession和当前运行的参数传递进去。
于是让我们看看这个execute方法的源码:
package org.apache.ibatis.binding; public class MapperMethod { private final SqlCommand command; private final MethodSignature method; public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, method); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { <span style="color:#FF0000;">result = executeForMany(sqlSession, args);//我们主要看看这个方法</span> } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { 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; } ........ //方法还是很多,我们不需要全看就看一个很常用的查询返回多条记录的 private <E> Object executeForMany(SqlSession sqlSession, Object[] args) { List<E> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.<E>selectList(command.getName(), param, rowBounds); } else { <span style="color:#FF0000;">result = sqlSession.<E>selectList(command.getName(), param);</span> } // issue #510 Collections & arrays support if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result; } ....... }
好这里我们看到,MapperMethod 类采用命令模式运行,然后根据上下文跳转可能跳转到许多方法中取,我们不需要全部明白,我们可以看到里面的executeForMany方法,再看看它的实现,实际上它最后就是通过sqlSession对象去运行对象的SQL而已。
至此,相信大家已经了解了MyBATIS为什么只用mappper接口便能够运行sql,因为mapperd的xml文件的命名空间对应的便是这个接口全路径,那么它根据全路径和方法名,便能够绑定起来,通过动态代理技术,让这个接口跑起来。
相关文章推荐
- difference between string literal and string object
- 【转】 Pro Android学习笔记(八一):服务(6):复杂数据Parcel
- 【转】 Pro Android学习笔记(八十):服务(5):访问远程服务
- Android draw、onDraw、dispatchDraw、invalidate、computeScroll 一些简要说明
- 【转】 Pro Android学习笔记(七九):服务(4):远程服务的实现
- 【转】 Pro Android学习笔记(七八):服务(3):远程服务:AIDL文件
- 【转】 Pro Android学习笔记(七七):服务(2):Local Service
- 【转】 Pro Android学习笔记(七六):服务(1):local和remote
- Dynamics CRM2016 新功能之从CRM APP通过电子邮件发送页面链接
- Dynamics CRM2016 新功能之从CRM APP通过电子邮件发送页面链接
- Dynamics CRM2016 新功能之从CRM APP通过电子邮件发送页面链接
- Dynamics CRM2016 新功能之从CRM APP通过电子邮件发送页面链接
- Android M中 JNI的入门学习
- android数据缓存
- swift2.0学习笔记之实现图片变圆
- 【转】 Pro Android学习笔记(六六):安全和权限(3):Provider权限
- android listview 实现从数据库读取已读功能,服务器通知增加,刷新listview
- 【转】 Pro Android学习笔记(六五):安全和权限(2):权限和自定义权限
- 【转】 Pro Android学习笔记(六四):安全和权限(1):签发apk
- 【转】 Pro Android学习笔记(六三):Preferences(7):代码控制首选项