阅读完需:约 23 分钟
前面介绍了SqlSessionFactory
和SqlSession
的构建过程,然后就可以通过SqlSession来获取Mapper对象了,所以这章来学习Mapper接口是如何通过动态代理来创建对象的。
Mapper动态代理
①、程序入口:

②、SqlSession
的实现类为DefaultSqlSession
,所以进入DeaultSqlSession
类找到重写的getMapper()方法。

可以发现它明显是调用了Configuration对象中的getMapper()方法来获取对应的接口对象。
③、所以点击进入Configuration类找到对应的getMapper()方法。

可以发现这里也是将工作继续交到MapperRegistry
的getMapper()的方法中,所以我们继续向下进行。
④、点击进入MapperRegistry
类找到getMapper()方法。

上面的代码中MapperProxyFactory
对象是通过knownMappers
来获取的,它是一个HashMap
,这个knownMapper的定义:

⑤、通过上面可以发现是使用MapperProxyFactory
来生成代理类,所以进入MapperProxyFactory
的newInstance
()方法中。

注意:这里调用了两个newInstance
()方法,上面的代码中进入后先调用第二个newInstance
方法并创建MapperProxy
代理对象,所以这里的重点是关注这个类。然后再去调用第一个newInstance
方法并将创建好的MapperProxy
代理对象传入进去,根据该对象创建代理类并返回。所以到这里已经得到需要的代理类了,但是我们的代理类所做的工作还得继续向下看MapperProxy
类。
获取Mapper的流程总结如下:

⑥、找到MapperProxy
类,这个类非常重要。可以发现这个类实现了InvocationHandler
接口,因为JDK的动态代理必须实现这个接口,只要类实现了InvocationHandler
接口的类最终都会执行invoke()方法,所以我们重点关注重写的invoke()方法。

MyBatis(3.5.3)版本不一样,但是最后都是调用mapperMethod.execute(sqlSession, args)
/**
* 方法实现说明:我们的Mapper接口调用我们的目标对象
* @author:xsls
* @param proxy 代理对象
* @param method:目标方法
* @param args :目标对象参数
* @return:Object
* @exception:
* @date:2019/8/27 19:15
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
/**
* 判断我们的方法是不是我们的Object类定义的方法,若是直接通过反射调用
*/
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) { //是否接口的默认方法
/**
* 调用我们的接口中的默认方法
*/
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
/**
* 真正的进行调用,做了二个事情
* 第一步:把我们的方法对象封装成一个MapperMethod对象(带有缓存作用的)
*/
final MapperMethod mapperMethod = cachedMapperMethod(method);
/**
*通过sqlSessionTemplate来调用我们的目标方法
* 那么我们就需要去研究下sqlSessionTemplate是什么初始化的
* 我们知道spring 跟mybatis整合的时候,进行了偷天换日
* 把我们mapper接口包下的所有接口类型都变为了MapperFactoryBean
* 然后我们发现实现了SqlSessionDaoSupport,我们还记得在整合的时候,
* 把我们EmployeeMapper(案例class类型属性为MapperFactoryBean)
* 的注入模型给改了,改成了by_type,所以会调用SqlSessionDaoSupport
* 的setXXX方法进行赋值,从而创建了我们的sqlSessionTemplate
* 而在实例化我们的sqlSessionTemplate对象的时候,为我们创建了sqlSessionTemplate的代理对象
* this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
*/
return mapperMethod.execute(sqlSession, args);
}
invoke()方法中首先判断代理对象是不是一个类,这里的Mapper代理对象是一个接口,不是一个类,所有会调用cachedInvoker(method).invoke()方法来生成MapperMethod对象,它是用来描述Mapper接口里面一个方法的内容的。有点类似于Spring中的BeanDefinition
类。(MapperMethod对象下面会有介绍)。
下面我们来看看这个MapperMethod
是如何生成的。它是调用了cachedInvoker
(method).invoke()方法来生成的(注意:这里是一个方法链,先是调用了cachedInvoker
(method),然后再调用的invoke
()),cachedInvoker(method)这个方法就不仔细看了,它的返回值为MapperMethodInvoker
对象,这个类是MapperProxy
类中定义的一个内部接口,并且定义了内部实现类,我们重点要看的是调用invoke()方法,如下。

可以发现这里定义了MapperMethod
对象,并且通过内部实现类重写的invoke()
方法调用了mapperMethod.execute()
方法,这个execute()
方法是具体执行操作的方法,然后将结果返回。
⑦、所以我们来具体看看MapperMethod
类。注意:我们首先需要知道这个MapperMethod类是干什么的?它用来描述Mapper接口里面一个方法的内容的,有点像Spring中的BeanDefinition
。MapperMethod
类是整个代理机制的核心类,它对SqlSession中的操作进行了封装使用,主要的功能是执行SQL的相关操作,例如执行sqlSession.selectList操作。该类里面定义了两个内部类分别为:SqlCommand
(Sql命令)和MethodSignature
(方法签名),其中SqlCommand听名字就知道是用来封装SQL命令的,是的,它是用来封装CRUD操作,也就是我们在xml中配置的操作的节点,每个节点都会生成一个MappedStatement
类。另一个MethodSignature
用来封装方法的参数以及返回类型。MapperMethod
在初始化时会实例化两个组件SqlCommand
(Sql命令)和MethodSignature
(方法签名),必须同时给这两个组件提供参数:Mapper的接口路径(mapperInterface),待执行的方法(method),配置的Configuration。然后通过获取SqlCommand中的执行类型,MapperMethod才知道该Mapper接口将要执行什么样的操作。


/**
* 方法实现说明:执行我们的目标方法
* @author:sqlSession:我们的sqlSessionTemplate
* @param args:方法参数
* @return:Object
* @exception:
* @date:2019/9/8 15:43
*/
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
/**
* 判断我们执行sql命令的类型
*/
switch (command.getType()) {
//insert操作
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
//update操作
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
//delete操作
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
//select操作
case SELECT:
//返回值为空
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//返回值是一个List
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
//返回值是一个map
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
//返回游标
result = executeForCursor(sqlSession, args);
} else {
//查询返回单个
/**
* 解析我们的参数
*/
Object param = method.convertArgsToSqlCommandParam(args);
/**
* 通过调用sqlSessionTemplate来执行我们的sql
* 第一步:获取我们的statmentName(com.tuling.mapper.EmployeeMapper.findOne)
* 然后我们就需要重点研究下SqlSessionTemplate是怎么来的?
* 在mybatis和spring整合的时候,我们偷天换日了我们mapper接口包下的所有的
* beandefinition改成了MapperFactoryBean类型的
* MapperFactoryBean<T> extends SqlSessionDaoSupport的类实现了SqlSessionDaoSupport
* 那么就会调用他的setXXX方法为我们的sqlSessionTemplate赋值
*
*/
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
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;
}
⑧、我们重点来看一下execute()方法中属性为SELECT时调用的executeForMany(SqlSession sqlSession, Object[] args)
方法,这个方法表示查询多条数据。

这是查询多条记录的一个方法,从上面的方法可以看到,虽然在SELECT操作中,是调用了MapperMethod
中的方法,但本质上仍是通过Sqlsession
下的selectList()
方法实现的,而其它增删改查都是类似的。最后经过一大圈的代理又回到了原地,这就是整个动态代理的实现过程了。
/**
* 方法实现说明
* @author:xsls
* @param statement: statementId
* @param parameter:参数对象
* @param rowBounds :mybiats的逻辑分页对象
* @return:
* @exception:
* @date:2019/9/9 20:33
*/
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/**
* 第一步:通过我们的statement去我们的全局配置类中获取MappedStatement
*/
MappedStatement ms = configuration.getMappedStatement(statement);
/**
* 通过执行器去执行我们的sql对象
* 第一步:包装我们的集合类参数
* 第二步:一般情况下是executor为cacheExetory对象
*/
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
然后,通过一层一层的调用,最终会来到doQuery方法, 这儿咱们就随便找个Excutor看看doQuery方法的实现吧,我这儿选择了SimpleExecutor:
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();
//内部封装了ParameterHandler和ResultSetHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
//StatementHandler封装了Statement, 让 StatementHandler 去处理
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
接下来,咱们看看StatementHandler
的一个实现类 PreparedStatementHandler
(这也是我们最常用的,封装的是PreparedStatement
), 看看它使怎么去处理的:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
//到此,原形毕露, PreparedStatement, 这个大家都已经滚瓜烂熟了吧
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
//结果交给了ResultSetHandler 去处理,处理完之后返回给客户端
return resultSetHandler.<E> handleResultSets(ps);
}
到此,整个调用流程结束

回到selectList(),我们可以看到执行的参数是通过command.getName()
来获取的,所以继续来跟踪command.getName()
是怎么来的,这里是SQLCommand的内容。
⑨、MapperMethod的内部类——SqlCommand,它封装了具体执行的动作。


/**
* 用户保存我们Mapper接口方法信息
*/
public static class SqlCommand {
/**
* 接口的方法名全路径比如:com.tuling.mapper.DeptMapper.findDepts
*/
private final String name;
/**
* 对应接口方法操作的sql类型(是insert|update|delte|select)
*/
private final SqlCommandType type;
/**
* 方法实现说明:创建我们的SqlCommand
* @author:xsls
* @param configuration:mybatis的全局配置
* @param mapperInterface:我们Mapper接口的class类型
* @param method:方法对象
* @return:
* @exception:
* @date:2019/9/6 21:51
*/
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
//获取我们的方法的名称
final String methodName = method.getName();
//方法所在接口的类型
final Class<?> declaringClass = method.getDeclaringClass();
/**
* 根据接口,方法名称解析出我们对应的mapperStatment对象
*/
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 {
//把我们的mappedStatmentID(com.tuling.mapper.EmpMapper.findEmp)
name = ms.getId();
//sql操作的类型(比如insert|delete|update|select)
type = ms.getSqlCommandType();
if (type == SqlCommandType.UNKNOWN) {
throw new BindingException("Unknown execution method for: " + name);
}
}
}
public String getName() {
return name;
}
public SqlCommandType getType() {
return type;
}
/**
* 方法实现说明:解析我们的mappedStatment对象
* @author:xsls
* @param mapperInterface:我们mapper接口的class类型
* @param methodName :方法名称
* @param declaringClass:方法所在类的接口
* @param configuration:mybatis的全局配置
* @return: MappedStatement
* @exception:
* @date:2019/9/8 13:29
*/
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
Class<?> declaringClass, Configuration configuration) {
//获取我们的sql对应的statmentId(com.tuling.mapper.DeptMapper.findDepts)
String statementId = mapperInterface.getName() + "." + methodName;
//根据我们的statmentId判断我们的主配置类是否包含 了我们的mapperStatment对象
if (configuration.hasStatement(statementId)) {
//存在通过key获取对应的mapperStatment对象返回
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
}
/**
* 获取我们mapper接口的父类接口
*/
for (Class<?> superInterface : mapperInterface.getInterfaces()) {
//判断方法所在的类是否实现了superInterface
if (declaringClass.isAssignableFrom(superInterface)) {
//解析我们父类的MappedStatment对象
MappedStatement ms = resolveMappedStatement(superInterface, methodName,
declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
首先是获取了方法名和类名,然后调用了resolveMappedStatement()
方法来解析生成MappedStatement
对象。所以我们重点来看看这个解析的方法,进入方法后可以看到首先就是构建了statementId
,注意到这个id是由接口名字+方法名字组成的。接着往下走,发现configuration.getMappedStatement(statementId);
这句话,参数传入的是statementId
,也就是说要找的MappedStatement
并不是new出来的,而是通过statementId
从Configuration
类对象中get出来的。也就是说很早之前MappedStatement
在很早之前就已经被初始化,并且放到Configuration对象里面,是的,我们的MappedStatement
在SqlSessionFactory
构建的时候就已经封装完成了,它包含了一条SQL语句的所有信息,MappedStatement
本身是一个Map,它的可以为接口名字+方法名字组成的,所以这里我们可以根据statementId来获取MappedStatement对象。最后根据获取的MappedStatement对象来初始化name和type的值。
⑩、MapperMethod的内部类——MethodSignature,它主要封装了Mapper接口中方法的参数类型、返回值类型等信息。

public static class MethodSignature {
private final boolean returnsMany;
private final boolean returnsMap;
private final boolean returnsVoid;
private final boolean returnsCursor;
private final boolean returnsOptional;
private final Class<?> returnType;
private final String mapKey;
private final Integer resultHandlerIndex;
private final Integer rowBoundsIndex;
private final ParamNameResolver paramNameResolver;
/**
* 方法实现说明:方法签名对象
* @author:xsls
* @param configuration:mybaits的全局配置类
* @param mapperInterface:我们mapper接口的class
* @param method:接口方法调用对象
* @return:
* @exception:
* @date:2019/9/8 13:45
*/
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
/**
* 解析方法的返回值类型
*/
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
//判断返回值是不是class类型的
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);
//返回值是不是optionnal类型的
this.returnsOptional = Optional.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);
/**
* 初始化我们参数解析器对象
*/
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
/**
* 方法实现说明:通过我们的参数解析器解析方法的参数
* @author:xsls
* @param args:参数数组
* @return: Object处理后的参数
* @exception:
* @date:2019/9/8 17:15
*/
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
public boolean hasRowBounds() {
return rowBoundsIndex != null;
}
public RowBounds extractRowBounds(Object[] args) {
return hasRowBounds() ? (RowBounds) args[rowBoundsIndex] : null;
}
public boolean hasResultHandler() {
return resultHandlerIndex != null;
}
public ResultHandler extractResultHandler(Object[] args) {
return hasResultHandler() ? (ResultHandler) args[resultHandlerIndex] : null;
}
public String getMapKey() {
return mapKey;
}
public Class<?> getReturnType() {
return returnType;
}
public boolean returnsMany() {
return returnsMany;
}
public boolean returnsMap() {
return returnsMap;
}
public boolean returnsVoid() {
return returnsVoid;
}
public boolean returnsCursor() {
return returnsCursor;
}
/**
* return whether return type is {@code java.util.Optional}.
* @return return {@code true}, if return type is {@code java.util.Optional}
* @since 3.5.0
*/
public boolean returnsOptional() {
return returnsOptional;
}
private Integer getUniqueParamIndex(Method method, Class<?> paramType) {
Integer index = null;
final Class<?>[] argTypes = method.getParameterTypes();
for (int i = 0; i < argTypes.length; i++) {
if (paramType.isAssignableFrom(argTypes[i])) {
if (index == null) {
index = i;
} else {
throw new BindingException(method.getName() + " cannot have multiple " + paramType.getSimpleName() + " parameters");
}
}
}
return index;
}
private String getMapKey(Method method) {
String mapKey = null;
if (Map.class.isAssignableFrom(method.getReturnType())) {
final MapKey mapKeyAnnotation = method.getAnnotation(MapKey.class);
if (mapKeyAnnotation != null) {
mapKey = mapKeyAnnotation.value();
}
}
return mapKey;
}
}
这里主要是对Mapper接口中的方法进行解析处理并且封装,它是通过参数解析器ParamNameResolver来完成的。
上面用到反射类Method中的很多方法,所以我们来简单掌握Method类常用的方法:
- getName():获取方法名
- getDeclaringClass:获取全限定类名
- getModifiers():获取权限修饰符
- getReturnType():获取返回类型
- getExceptionTypes():获取所有抛出的异常类型
- getParameterTypes():获取所有参数的类型
- getParameterAnnotations():获取方法中的所有注解
- getAnnotations():获取方法级别的注解
至此mapper的动态代理介绍完了。下面还介绍了Mapper接口中的方法参数是怎么来获取的,它是通过参数解析器(ParamNameResolver)来完成的,有兴趣可以了解一下。
⑪、在上述代码中经常能看见这样一句代码 method.convertArgsToSqlCommandParam(args);
这个方法是MethodSignature
内部类中的,该方法主要的功能是获取@Param注解上的参数值。而实现方式便是通过参数解析器(ParamNameResolver),convertArgsToSqlCommandParam(args)
方法实际上调用的是ParamNameResolver
下的getNamedParams(args)
方法。在分析该方法之前,先看看构造器都做了些什么操作。


构造器同样需要两个入参,配置类和方法名,ParamNameResolver
类下包含了两个属性字段GENERIC_NAME_PREFIX
属性前缀和参数集合names,构造器会将Method中所有参数级别的注解全部解析出来方法有序参数集中,names中存储形式为<参数下标,参数名>,如果在注解上设置了参数名,则会直接获取注解的value值,如果没有使用@Param注解,则使用真实的参数名,注意:真实参数名其实是arg0,arg1….的形式展现的,在判断真实参数名时,Mybatis会检查JDK版本是否包含java.lang.reflect.Parameter类,不存在该类的化会抛出ClassNotFoundException异常。完成初始化后,就可以调用getNamedParams(args)方法了,如下代码所示,该方法使用了类中的names属性,从有序集合中取出所有的<参数索引,参数名>键值对>,随后填充到另外一个集合中,以<参数名,参数下标索引,>形式呈现,同时会保留一份<param+下标索引,参数下标>的键值对。

public Object getNamedParams(Object[] args) {
//获取参数的个数
/**
* names的数据结构为map
* ({key="0",value="id"},{key="1",value="name"})
*/
final int paramCount = names.size();
//若参数的个数为空或者个数为0直接返回
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
/**
* 若有且只有一个参数 而且没有标注了@Param指定方法方法名称
*
*/
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<>();
int i = 0;
/**
* 循坏我们所有的参数的个数
*/
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//把key为id,value为1加入到param中
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
//加入通用的参数:名称为param+0,1,2,3......
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
//把key为param+0,1,2,3.....,value值加入到param中
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
简单总结
这边结合获取SqlSession的流程,做下简单的总结:
获得SqlSession的流程:
-
SqlSessionFactoryBuilder
解析配置文件,包括属性配置、别名配置、拦截器配置、环境(数据源和事务管理器)、Mapper配置等;解析完这些配置后会生成一个Configration
对象,这个对象中包含了MyBatis需要的所有配置,然后会用这个Configration对象创建一个SqlSessionFactory
对象,这个对象中包含了Configration
对象; - 拿到
SqlSessionFactory
对象后,会调用SqlSessionFactory
的openSesison
方法,这个方法会创建一个Sql执行器(Executor组件中包含了Transaction对象),这个Sql执行器会代理你配置的拦截器方法。 - 获得上面的Sql执行器后,会创建一个
SqlSession
(默认使用DefaultSqlSession),这个SqlSession中也包含了Configration对象和上面创建的Executor对象,所以通过SqlSession也能拿到全局配置; - 获得SqlSession对象后就能执行各种CRUD方法了。
Sql的执行流程:
-
调用SqlSession的getMapper方法,获得Mapper接口的动态代理对象
MapperProxy
,调用Mapper接口的所有方法都会调用到MapperProxy
的invoke方法(动态代理机制); -
MapperProxy的invoke方法中唯一做的就是创建一个
MapperMethod
对象,然后调用这个对象的execute
方法,sqlSession会作为execute方法的入参; - 往下,层层调下来会进入
Executor
组件(如果配置插件会对Executor进行动态代理)的query方法,这个方法中会创建一个StatementHandler
对象,这个对象中同时会封装ParameterHandler
和ResultSetHandler
对象。调用StatementHandler预编译参数以及设置参数值,使用ParameterHandler来给sql设置参数。
Executor组件有两个直接实现类,分别是BaseExecutor
和CachingExecutor
。CachingExecutor静态代理了BaseExecutor。Executor组件封装了Transction组件,Transction组件中又分装了Datasource组件。
- 调用
StatementHandler
的增删改查方法获得结果,ResultSetHandler
对结果进行封装转换,请求结束。
Executor、StatementHandler 、ParameterHandler、ResultSetHandler,Mybatis的插件会对上面的四个组件进行动态代理。
重要类
-
MapperRegistry
:本质上是一个Map,其中的key是Mapper接口的全限定名,value的MapperProxyFactory; -
MapperProxyFactory
:这个类是MapperRegistry中存的value值,在通过sqlSession获取Mapper时,其实先获取到的是这个工厂,然后通过这个工厂创建Mapper的动态代理类; -
MapperProxy
:实现了InvocationHandler接口,Mapper的动态代理接口方法的调用都会到达这个类的invoke方法; -
MapperMethod
:判断你当前执行的方式是增删改查哪一种,并通过SqlSession执行相应的操作; -
SqlSession
:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能; -
Executor
:MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护;-
StatementHandler
:封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。 -
ParameterHandler
:负责对用户传递的参数转换成JDBC Statement 所需要的参数。 -
ResultSetHandler
:负责将JDBC返回的ResultSet结果集对象转换成List类型的集合; -
TypeHandler
:负责java数据类型和jdbc数据类型之间的映射和转换 -
MappedStatement
:MappedStatement维护了一条节点的封装 -
SqlSource
:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回 -
BoundSql
:表示动态生成的SQL语句以及相应的参数信息 -
Configuration
:MyBatis所有的配置信息都维持在Configuration对象之中。
-
调试主要关注点
-
MapperProxy.invoke
方法:MyBatis的所有Mapper对象都是通过动态代理生成的,任何方法的调用都会调到invoke方法,这个方法的主要功能就是创建MapperMethod对象,并放进缓存。所以调试时我们可以在这个位置打个断点,看下是否成功拿到了MapperMethod对象,并执行了execute方法。 -
MapperMethod.execute
方法:这个方法会判断你当前执行的方式是增删改查哪一种,并通过SqlSession执行相应的操作。Debug时也建议在此打个断点看下。 -
DefaultSqlSession.selectList
方法:这个方法获取了获取了MappedStatement对象,并最终调用了Executor的query方法;
数据库执行过程

二级缓存原理
