首先以如下测试代码进行追踪
public class TestMybatis {
public static void main(String[] args) throws IOException {
InputStream stream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(stream);
SqlSession sqlSession = build.openSession();
TestMapper mapper = sqlSession.getMapper(TestMapper.class);
Goods goods = mapper.queryById(7);
sqlSession.commit();
System.out.println(goods);
}
}
一、启动解析配置文件
mybatis以SqlSessionFactoryBuilder为入口,在其bulid方法中,创建XMLConfigBuilder,解析配置文件,以下是调用XMLConfigBuilder中的parse方法,在parse方法中解析/configuration的根节点
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
mapperElement方法主要负责mappers节点的解析,主要有两种方法的解析,
一种是package子节点配置的name属性,也就是通过包扫描的方法加载mapper;
另外一种是mapper子节点,通过配置resource、url或者class任意一种方法,去解析对应的mapper
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
以package的方式为例,扫描指定包下的所有类,然后过滤出所有的接口类;创建一个MapperProxyFactory的代理工厂并放入Map<Class<?>, MapperProxyFactory<?>>容器中,然后创建一个MapperAnnotationBuilder扫描所有包含Select.class,SelectProvider.class等等这些注解的接口方法,调用MapperAnnotationBuilder的parse方法解析
package方法配置的,会通过接口的全路径名称,找到其对应的xml配置文件,然后再使用XMLMapperBuilder解析xml配置文件
private void loadXmlResource() {
if (!this.configuration.isResourceLoaded("namespace:" + this.type.getName())) {
String xmlResource = this.type.getName().replace('.', '/') + ".xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(this.type.getClassLoader(), xmlResource);
} catch (IOException var4) {
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, this.assistant.getConfiguration(), xmlResource, this.configuration.getSqlFragments(), this.type.getName());
xmlParser.parse();
}
}
}
最终所有的配置解析完成之后,由Configuration统一管理所有的静态配置。Mapper.xml的所有内容都放置在MappedStatement中
二、openSession
调用DefaultSqlSessionFactory的openSessionFromDataSource方法,该方法中,主要做了以下事情:
1.创建事务
2.创建执行器Executor
3.创建包含执行器和事务的DefaultSqlSession对象
通过Configuration的newExecutor方法创建执行器,默认创建SimpleExecutor,如果配置了二级缓存,那么创建CachingExecutor执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
//插件的调用
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
三、创建Mapper的代理对象
首先获取第一步创建的MapperProxyFactory,通过MapperProxyFactory的newInstance方法创建MapperProxy对象,然后Mapper的代理对象
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(),
new Class[]{this.mapperInterface}, mapperProxy);
}
四、执行sql语句(以查询方法为例)
调用MapperProxy的invoke方法,然后调用其excute方法,决定执行的方法类型,这里调用的queryById方法最终执行的是sqlSession.selectOne->selectList->CachingExecutor.query方法
如果启用二级缓存,那么查询时首先会去二级缓存中获取数据,如果没有获取到才会执行后续查询操作
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
this.flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, parameterObject, boundSql);
List<E> list = (List)this.tcm.getObject(cache, key);
if (list == null) {
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
this.tcm.putObject(cache, key, list);
}
return list;
}
}
return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
没有查询到数据,执行BaseExecutor的query方法;调用this.localCache.getObject(key)从一级缓存中获取,没有查询到则进行数据库的查询
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
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);
}
return list;
}
进入SimpleExecutor的doQuery方法,执行Configuration的newStatementHandler创建一个StatementHandler。
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
在StatementHandler创建过程中,执行Configuration的newParameterHandler和newResultSetHandler分别创建ParameterHandler和ResultSetHandler,并且分别执行对应的插件
执行完StatementHandler的创建,执行SimpleExecutor的prepareStatement,获取数据库的链接,进行预编译以及参数的设置
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Connection connection = this.getConnection(statementLog);
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
最后调用PreparedStatementHandler的query方法,通过PreparedStatement 执行sql并将结果交于ResultSetHandler处理,返回最终的结果
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
ps.execute();
return this.resultSetHandler.handleResultSets(ps);
}
五、二级缓存
二级缓存的获取是通过TransactionalCacheManager的TransactionalCache获取,然后依次通过下列顺序逐级获取。
org.apache.ibatis.cache.TransactionalCacheManager#getObject
org.apache.ibatis.cache.decorators.TransactionalCache#getObject
org.apache.ibatis.cache.decorators.SynchronizedCache#getObject
org.apache.ibatis.cache.decorators.LoggingCache#getObject
org.apache.ibatis.cache.decorators.SerializedCache#getObject
org.apache.ibatis.cache.decorators.LruCache#getObject
org.apache.ibatis.cache.impl.PerpetualCache#getObject
但是在执行putObject的操作的时候,并没有像获取时那样逐级存入,而是存在了一个名为entriesToAddOnCommit的Map集合中。在最终执行sqlSession的commit操作或者close方法的时候,才会放入最终的PerpetualCache中。因为二级缓存真正生效是在会话提交或者关闭的时候,这样做的目的也是为了防止脏读。
org.apache.ibatis.cache.TransactionalCacheManager#putObject
org.apache.ibatis.cache.decorators.TransactionalCache#putObject