一、概述
MapperMethod,看下MapperMethod源码的实现,主要是下面这个方法:
后面执行又跟到SimpleExecutor类的query方法:
最后到类PreparedStatementHandler里面:
注:因为源码还是很复杂的,我也只能大致看个大概,同时写这边文章也是方便自己记忆,所以可能会有理解出错
的地方,还望各位大神指导
用spring+mybatis搭建项目的时候,刚开始就有个困惑,Mapper只有接口,没有实现,那么怎么样实现底层的sql查询呢,后来网上查资料,加上有了设计模式的相关知识后,才知道用的是java的动态代理技术生成了代理类MapperProxy,mybatis的运行逻辑上分为二部分,一部分根据接口生成接口动态代理类,二是执行接口的方法时,实际上执行的是接口的动态代理的invoke方法,执行参数绑定、操作、返回参数的解析;
二、源码解析:
我们都知道mybatis的执行离不开sqlsession,那么代理类又是如何多SqlSession进行封装的呢?带着这些疑问,让我们通过分析源代码的方式来解释这些问题。
sqlSession的调用:
//获取session
SqlSession session = sqlSessionFactory.openSession();
//获取mapper接口的代理对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//调用代理对象方法
User user = userMapper.findUserById(27);
System.out.println(user);
//关闭session
session.close();
System.out.println("---------执行完毕-----------");
我们主要看下面这个方法
UserMapper userMapper = session.getMapper(UserMapper.class);
@Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); }这个是上面getMapper的实现,跟进代码里面发现了MapperRegistry类,
mybatis封装mapper接口的主要有四个类,位于org.apache.ibatis.binding包下面,MapperRegistry是注册代理接口与获取代理类实例的工具类,如下:
/** * 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 org.apache.ibatis.builder.annotation.MapperAnnotationBuilder; import org.apache.ibatis.io.ResolverUtil; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSession; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author Clinton Begin * @author Eduardo Macarron * @author Lasse Voss */ public class MapperRegistry { //全局配置文件对象 private final Configuration config; //一个Map,key是Mapper类型的Class类型对象,value是创建Mapper代理对象的工厂 private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>(); public MapperRegistry(Configuration config) { this.config = config; } //根据Mapper的Class类型对象和SqlSession对象得到Mapper的代理对象 @SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { //根据type获取mapper代理对象工厂的实例,如果没有获取到,抛出异常 final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { //创建一个当前接口的代理对象传到sqlSession return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } } public <T> boolean hasMapper(Class<T> type) { return knownMappers.containsKey(type); } public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory<T>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } /** * @since 3.2.2 */ public Collection<Class<?>> getMappers() { return Collections.unmodifiableCollection(knownMappers.keySet()); } /** * @since 3.2.2 */ public void addMappers(String packageName, Class<?> superType) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses(); for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } } /** * @since 3.2.2 */ public void addMappers(String packageName) { addMappers(packageName, Object.class); } }
MapperProxyFactory对象代码如下:
/** * 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.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.ibatis.session.SqlSession; /** * @author Lasse Voss */ public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); 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) { //用jdk的动态代理技术生成要代理的mapper接口的动态代理类; return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } //传入sqlSession,生成MapperProxy的代理类,MapperProxy实现了jdk的动态代理接口InvocationHandler public T newInstance(SqlSession sqlSession) { //创建一个代理类对象,设置SqlSession的值,MapperProxy是一个动态代理对象 final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } } 以上是mybatis初始化的的时候做的事情,但是对于真正执行sql操作的时候又做了哪些操作呢,当调用mapper 接口时,实际调用的是接口的动态代理的invoke方法,先看下代理对象MapperProxy源码如下,入口是invoke方法;
/** * Copyright 2009-2017 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.invoke.MethodHandles; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Map; import org.apache.ibatis.lang.UsesJava7; 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 { 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); } 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; } @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(); } }在cachedMapperMethod方法里面,先判断缓存里面是否已经缓存了MapperMethod,如果没有,则新建一个
MapperMethod,看下MapperMethod源码的实现,主要是下面这个方法:
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) { this.command = new SqlCommand(config, mapperInterface, method); this.method = new MethodSignature(config, mapperInterface, method); }
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) { final String methodName = method.getName(); final Class<?> declaringClass = method.getDeclaringClass(); //根据接口名+方法名获取MappedStatement,一条MappedStatement记录就是mybatis中配置的一条 //sql记录,包含id=namespace+sql的id,操作类型(select、delete、update)等等一系列信息; MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass, configuration); if (ms == null) { if (method.getAnnotation(Flush.class) != null) { name = null; type = SqlCommandType.FLUSH; } else { throw new BindingException("Invalid bound statement (not found): " + mapperInterface.getName() + "." + methodName); } } else { name = ms.getId();//com.newnoa.api.mapper.ApiMapper.getApiByServiceAndPort type = ms.getSqlCommandType();//SELECT if (type == SqlCommandType.UNKNOWN) { throw new BindingException("Unknown execution method for: " + name); } } }
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) { //解析返回类型,一般是自定义类型 ,例如:class com.newnoa.api.entity.po.ApiDB Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); if (resolvedReturnType instanceof Class<?>) { this.returnType = (Class<?>) resolvedReturnType; } else if (resolvedReturnType instanceof ParameterizedType) { this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType(); } else { this.returnType = method.getReturnType(); } //返回是否为空 this.returnsVoid = void.class.equals(this.returnType); //放回类型是否多参数 this.returnsMany = (configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray()); this.returnsCursor = Cursor.class.equals(this.returnType); this.mapKey = getMapKey(method); this.returnsMap = (this.mapKey != null); this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); //通过param注解获得参数的名称和顺序,名称和xml中定义的要相同 this.paramNameResolver = new ParamNameResolver(configuration, method); }接着就是执行MapperMethod的execute方法了:
public Object execute(SqlSession sqlSession, Object[] args) { Object result; //根据command操作来决定是什么操作,例如:select、update、insert、update 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()) {//返回Map result = executeForMap(sqlSession, args); } else if (method.returnsCursor()) {//返回游标 result = executeForCursor(sqlSession, args); } else { //组装参数,通过前面得到的参数名称和序号和方法执行传进来的参数拼装成一个map //key是参数的名称,value是参数的值 Object param = method.convertArgsToSqlCommandParam(args); //这个大家应该很熟悉了,就是通过mybatis的xml原始的调用方式 //com.newnoa.api.mapper.ApiMapper.getApiByServiceAndPort 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; }后面debug跟到了DefaultSqlSession类的方法,其实selectOne底层调用的也是selectList方法:
@Override public <T> T selectOne(String statement, Object parameter) { // Popular vote was to return null on 0 results and throw exception on too many. List<T> list = this.<T>selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; } }
后面执行又跟到SimpleExecutor类的query方法:
@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
最后到类PreparedStatementHandler里面:
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }这是debug后得到statement的结果,可以看出mybatis底层就是封装的jdbc:
注:因为源码还是很复杂的,我也只能大致看个大概,同时写这边文章也是方便自己记忆,所以可能会有理解出错
的地方,还望各位大神指导