MyBatis框架源码解析
MyBatis是一个优秀的持久层框架,通过简洁的XML配置方式就能消除以前传统
使用JDBC出现大量重复的代码,以及参数的设置和结果集的映射. 如果能够懂的底层代码和原理,在使用上就会更得心应手。而且出现错误,也能更快的找出错误的原因。解析源码
还有个好处,就是能够看一下大神的设计思想。这些设计思想也可以运用到实际开发中,而且如果自己明白框架怎么写之后,自己也可以尝试去写一个开源框架出来; 同时,阅读解析源码,也是高级程序员必备的技能之一;
下面我们就从一个简单的示例开始源码的分析:
public static void main(String[] args) {
String resource = "Configuration.xml"; Reader reader;
try { //解析配置文件得到Reader reader = Resources.*getResourceAsReader*(resource); //通过配置文件得到SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); //得到SqlSession SqlSession session = sqlSessionFactory.openSession();
try { //调用查询方法 完成封装 List<User> users = session.selectList("cn.itsource.mapper.UserMapper.getUser"); users.forEach(user->{
System.*out*.println(user); }); } finally {
session.close(); }
} catch (IOException e) {
e.printStackTrace(); }
}
(1) 第一步 得到SqlSessionFactory对象 ,关键方法 SqlSessionFactoryBuilder().build(reader).我们跟进源码SqlSessionFactoryBuilder类的build方法:
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
// XMLConfigBuilder XML配置解析类
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
//parser.parse()解析结果放入Configuration里面
//build方法得到DefaultSqlSessionFactory
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException var13) {
;
}
}
return var5;
}
通过build方法 最终得到DefaultSqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
已经得到SqlSessionFactory对象了,而且把配置文件信息存入了DefaultSqlSessionFactory 这个类的config变量上;
(2)通过 sqlSessionFactory.openSession()得到SqlSession对象
源码如下:
public SqlSession openSession() {
通过该方法得到SqlSession
return this.openSessionFromDataSource(this.configuration.getDefaultExecutorType(), (TransactionIsolationLevel)null, false);
}
openSessionFromDataSource方法如下:
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
//从配置类里面得到环境变量
Environment environment = this.configuration.getEnvironment();
//得到事务配置
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
//得到事务对象
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//通过事务得到执行器
Executor executor = this.configuration.newExecutor(tx, execType, autoCommit);
//通过执行器得到DefaultSqlSession对象-关键代码
var8 = new DefaultSqlSession(this.configuration, executor);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
最终得到一个SqlSession对象(DefaultSqlSession是SqlSession实现类)
(3)通过sqlSession对象调用SelectList方法返回数据
List<User> users = session.selectList("cn.itsource.mapper.UserMapper.getUser");
我们进入核心的selectList方法里面去:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var6;
try {
//从配置得到Mapper语句对象 –解析mapper.xml文件
MappedStatement ms = this.configuration.getMappedStatement(statement);
//通过执行器调用query方法 封装数据 返回结果
List<E> result = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
var6 = result;
} catch (Exception var10) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var10, var10);
} finally {
ErrorContext.instance().reset();
}
return var6;
}
Query方法最终调用BaseExecutor里面query方法:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//动态解析sql
BoundSql boundSql = ms.getBoundSql(parameter);
//缓存
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
//查询
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
在这个方法里面做了下面一些事情:
(1) 解析动态的sql
(2) 加入缓存里面
(3) 进行查询
public <E> List<E> 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 i$ = this.deferredLoads.iterator();
while(i$.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
从上面的源码中 可以看出最终调用:queryFromDatabase方法
这个方法最终会调用SimpleExecutor的doQuery方法
public <E> List<E> 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, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
//通过handler调用query查询方法
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
最终调用PreparedStatementHandler类的query方法
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
//处理结果的封装
return this.resultSetHandler.handleResultSets(ps);
}
大致的流程图:
得到SqlSession的过程
![源码时代](https://upload-images.jianshu.io/upload_images/13211021-7597f31d5a4e9f10.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
调用方法的过程
![源码时代](https://upload-images.jianshu.io/upload_images/13211021-98a7170096fd3650.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
封装数据过程:
![源码时代](https://upload-images.jianshu.io/upload_images/13211021-5de519f221f00850.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
好了,这次源码分析就到这个地方; 通过这次源码剖析,我们看到了mybatis从加载配置文件到调用方法以及返回数据进行封装的整个过程; 从源码里面我们看出,框架底层分别使用很多类和接口来完成开发,而且具备单一职责的原则,一个类就做一个事情,通过层层组合,就完成整套流程,当然 这次分析源码,就是一部分内容。下次在继续;
转载于:https://www.jianshu.com/p/5fb93f0c4857
推荐阅读
-
axios 源码解析(下) 拦截器的详解
-
Spring源码解析之ConfigurableApplicationContext
-
angularjs 源码解析之injector
-
angularjs 源码解析之scope
-
netty源码解析(4.0)-28 ByteBuf内存池:PooledByteBufAllocator-把一切组装起来
-
SpringBoot 源码解析 (七)----- Spring Boot的核心能力 - SpringBoot如何实现SpringMvc的?
-
SpringBoot 源码解析 (六)----- Spring Boot的核心能力 - 内置Servlet容器源码分析(Tomcat)
-
MyBatis启动之XMLConfigBuilder解析配置文件(二)
-
解析Mac OS下部署Pyhton的Django框架项目的过程
-
Django框架实现逆向解析url的方法