https://blog.youkuaiyun.com/ykzhen2015/article/details/50314509
我们目前在MyBATIS中,我们知道MyBATIS的Mapper是一个接口,而不是一个实体类。在Java中接口是没有办法运行的。那么它是怎么运行的呢?
有了第一篇的基础,我们可以大胆的想象——它是通过动态代理运行,没有错真实的情况就是这样的。
让我们看看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;
- }
- }
于是让我们看看这个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文件的命名空间对应的便是这个接口全路径,那么它根据全路径和方法名,便能够绑定起来,通过动态代理技术,让这个接口跑起来。