return method.invoke(this, args);
}
//接口默认方法执行
if (method.isDefault()) {
if (privateLookupInMethod == null) {
return this.invokeDefaultMethodJava8(proxy, method, args);
}
return this.invokeDefaultMethodJava9(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
但最终会调用倒忙mapperMethod::execute() 方法执行,主要是判断是 INSERT 、 UPDATE 、 DELETE 、 SELECT 语句去操作,其中如果是查询的话,还会判断返回值的类型。
public Object execute(SqlSession sqlSession, Object[] args) {
Object result; Object param; switch(this.command.getType()) {
case INSERT: param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE: param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE: param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT: if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result); } } break;
case FLUSH: result = sqlSession.flushStatements(); break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
} if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException(“Mapper method '” + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + “).”);
} else {
return result;
} }
通过以上的分析,总结出
-
Mapper 接口实际对象为代理对象 MapperProxy ;
-
MapperProxy 继承 InvocationHandler ,实现 invoke 方法;
-
MapperProxyFactory::newInstance() 方法,基于 JDK 动态代理的方式创建了一个 MapperProxy 的代理类;
-
最终会调用倒忙mapperMethod::execute() 方法执行,完成操作。
-
而且更重要一点是, MyBatis 使用的动态代理和普遍动态代理有点区别,没有实现类,只有接口,MyBatis 动态代理类图结构如下所示:
以 SELECT 为例, 调用会 SqlSession ::selectOne() 方法。继续往下执行,会执行 Executor::query() 方法。
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5; try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9);
} finally {
ErrorContext.instance().reset(); } return var5;
}
执行倒忙Executor 类,那么我们来看看其究竟有什么?
E x ecutor
Executor 对象为SQL 的执行引擎,负责增删改查的具体操作,顶层接口 SqlSession 中都会有一个 Executor 对象,可以理解为 JDBC 中 Statement 的封装版。
Executor 是最顶层的是执行器,它有两个实现类,分别是 BaseExecutor 和 CachingExecutor
-
BaseExecutor 是一个抽象类,实现了大部分 Executor 接口定义的功能,降低了接口实现的难度。 BaseExecutor 基于适配器设计模式之接口适配会有三个子类,分别是 SimpleExecutor 、 ReuseExecutor 和 BatchExecutor 。
-
BatchExecutor : 批处理执行器,用于执行update(没有select,JDBC批处理不支持select将多个 SQL 一次性输出到数据库,
-
ReuseExecutor : 可重用执行器, 执行 update 或 select ,以sql作为 key 查找 Statement 对象,存在就使用,不存在就创建,用完后,不关闭 Statement 对象,而是放置于 Map<String, Statement> 内,供下一次使用。简言之,就是重复使用 Statement 对象
-
SimpleExecutor : 是 MyBatis 中 默认 简单执行器,每执行一次 update 或 select ,就开启一个 Statement 对象,用完立刻关闭 Statement 对象
-
CachingExecutor : 缓存执行器,为 Executor 对象增加了 二级缓存 的相关功:先从缓存中查询结果,如果存在就返回之前的结果;如果不存在,再委托给 Executor delegate 去数据库中取, delegate 可以是上面任何一个执行器。
在 Mybatis 配置文件中,可以指定默认的 ExecutorType 执行器类型,也可以手动给 DefaultSqlSessionFactory 的创建 SqlSession 的方法传递 ExecutorType 类型参数。
看完 Exector 简介之后,继续跟踪执行流程链路分析, SqlSession 中的 JDBC 操作部分最终都会委派给 Exector 实现, Executor::query() 方法,看看在 Exector 的执行是怎样的?
每次查询都会先经过 CachingExecutor 缓存执行器, 会先判断二级缓存中是否存在查询 SQL ,如果存在直接从二级缓存中获取,不存在即为第一次执行,会直接执行SQL 语句,并创建缓存,都是由 CachingExecutor::query() 操作完成的。
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { //获取查询语句对应的二级缓存
Cache cache = ms.getCache(); //sql查询是否存在在二级缓存中
if (cache != null) {
//根据 节点的配置,判断否需要清空二级缓存
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql);
//查询二级缓存
List list = (List)this.tcm.getObject(cache, key);
if (list == null) {
//二级缓存没用相应的结果对象,调用封装的Executor对象的 query() 方法
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//将查询结果保存到二级缓存中
this.tcm.putObject(cache, key, list);
} return list;
} }//没有启动二级缓存,直接调用底层 Executor 执行数据数据库查询操作
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
如果在经过 CachingExecutor 缓存执行器(二级缓存)没有返回值的话,就会执行 BaseExecutor 以及其的实现类,默认为 SimpleExecutor ,首先会在一级缓存中获取查询结果,获得不到,最终会通过 SimpleExecutor:: () 去数据库中查询。
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity(“executing a query”).object(ms.getId());
if (this.closed) {
throw new ExecutorException(“Executor was closed.”);
} else {
//是否清除本地缓存
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
} List list; try {
++this.queryStack;
//从一级缓存中,获取查询结果
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
//获取到结果,则进行处理
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//获得不到,则从数据库中查询
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
} } finally {
–this.queryStack;
} if (this.queryStack == 0) {
//执行延迟加载
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next(); deferredLoad.load(); } this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
} } return list;
} }
那么 SimpleExecutor::doQuery() 如何去数据库中查询获取到结果呢? 其实执行到这边mybatis的执行过程就从 Executor 转交给 StatementHandler 处理,
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9; try {
Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler); } finally {
this.closeStatement(stmt);
} return var9;
}
这样我们的执行链路分析已到 StatementHandler 了,现在让我们去一探究竟其原理
StatementHandler
StatementHandler 负责处理 Mybatis 与JDBC之间 Statement 的交互,即 Statement 对象与数据库进行交互,其为顶级接口,有4个实现类,其中三个是 Statement 对象与数据库进行交互类, 另外一个是路由功能的,
-
RoutingStatementHandler : 对 Statement 对象没有实际操作,主要负责另外三个 StatementHandler 的创建及调用, 而且在 MyBatis 执行时,使用的 StatementHandler 接口对象实际上就是 RoutingStatementHandler 对象。
-
SimpleStatementHandler : 管理 Statement 对象, 用于简单SQL的处理 。
-
PreparedStatementHandler : 管理 Statement 对象,预处理SQL的接口 。
-
CallableStatementHandler :管理 Statement 对象,用于执行存储过程相关的接口 。
在经历过 Executor 后,基于初始化加载到 MapperState 中的 StatementType 的类型通 过Configuration.newStatementHandler() 方法中的 RoutingStatementHandler 生成 StatementHandler 实际处理类。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch(ms.getStatementType()) {
case STATEMENT:
this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后总结
搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析
最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
更新!**
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后总结
搞定算法,面试字节再不怕,有需要文章中分享的这些二叉树、链表、字符串、栈和队列等等各大面试高频知识点及解析
最后再分享一份终极手撕架构的大礼包(学习笔记):分布式+微服务+开源框架+性能优化
[外链图片转存中…(img-28F8CBFm-1712536380829)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!