Mybatis使用也有好多年了,还没仔细扒一下源码,前段时间有空,看了一下源码,做个简单的记录。源码解析包括了一些设计思路,设计模式,和一些自己的想法。
1 入手-入口
// 加载Mybatis配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过构建工厂,构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建一个sqlSession
SqlSession session = sqlSessionFactory.openSession();
try {
// 通过sqlSession先获取到Mapper接口对象
BlogMapper mapper = session.getMapper(Mapper.class);
// 执行查询语句
Blog one = mapper.selectOne();
System.out.println(one);
} finally {
// 关闭当前session
session.close();
}
通过入口配置,我们要知道整个Mybatis运行的流程,代码注释上已写明。接下来我们一步一步的分析。
2 加载配置文件
Resources.getResourceAsStream("mybatis-config.xml")
这个代码,只是用来加载配置文件,并没有做解析。
我们点进去看下源码,发现首先是获取所有的可能得类加载器,然后使用类加载器加载配置文件
public InputStream getResourceAsStream(String resource) {
return getResourceAsStream(resource, getClassLoaders(null));
}
使用类加载器加载文件后直接返回InputStream,第一步文件加载就完成了
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
for (ClassLoader cl : classLoader) {
if (null != cl) {
// try to find the resource as passed
InputStream returnValue = cl.getResourceAsStream(resource);
// now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
if (null == returnValue) {
returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) {
return returnValue;
}
}
}
return null;
}
3 构建sqlSessionFactory工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
这个代码就是将上一步的inputStream流作为参数传进去,build()方法有很多重载,可以配置其他的参数,这里使用的是一个参数的。这里有个设计模式是建造者模式。
建造者模式:使用多个简单的对象一步一步构建成一个复杂的对象
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
构建XMLConfigBuilder对象用于解析XML,XPathParser是XMLConfigBuilder调用的解析工具,最后通过
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(inputStream));
}
createDocument方法是最终创建文档的方法,创建好文档等候,就是解析,调用parseConfiguration方法解析document里面的节点
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
解下好的参数会封装到Configuration configuration配置参数中,便于后面使用。
解析好的configuration会调用build方法,生成SqlSessionFactory返回,至此SqlSessionFactory创建成功。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
4 创建SqlSession
使用DefaultSqlSessionFactory的openSessionFromDataSource()方案创建sqlSession,其中ExecutorType默认是SIMPLE普通类型。
SqlSession session = sqlSessionFactory.openSession();
根据Configuration配置的信息(Environment环境,数据源,事务等信息)(Executor执行器,默认是SimpleExecutor用于执行查询语句的)返回创建好的DefaultSqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level,
boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
下面使用sqlSession就可以加载mapper对象,操作数据库了。
5 加载Mapper,执行查询
通过反射和类型创建Mapper代理对象,从前面xml配置好的mapper中读取(mapperRegistry)
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
- 从DefaultSqlSession中的Configuration属性中获取数据
- Configuration中有mapperRegistry对象
- mapperRegistry中knownMappers是类型是Map<Class<?>, MapperProxyFactory<?>>的map对象
- knownMappers.get(type)获取mapper的代理对象
- 获取到的对象类型是MapperProxyFactory
- MapperProxyFactory的方法缓存(methodCache)中又包含MapperMethodInvoker,这个就是具体的代理执行器,然后创建MapperProxy
- MapperProxy中有一个cachedInvoker方法
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (!m.isDefault()) {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
MapperMethod方法会解析mapper中的语句
SqlCommand方法会调用resolveMappedStatement方法生成MappedStatement
6 MapperProxy代理
这个类中有一个invoke方法是具体执行查询语句的方法
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
return mapperMethod.execute(sqlSession, args);
}
会自动判断类型,执行响应的查询语句
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
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()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
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;
}
然后执行流程大概就是:
- 执行响应的handler处理对应的查询语句
- handler调用执行器executor只能执行语句
- 如果是CachingExecutor会调用createCacheKey创建缓存key放入map
- 然后对查询的结果封装结果集进行返回
7. 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) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
TransactionalCacheManager事务缓存管理器
tcm.getObject(cache, key)更具缓存和key获取已缓存的数据返回,这就是二级缓存
8. BaseExecutor的Query逻辑
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 (closed) {
throw new ExecutorException("Executor was closed.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
localCache就是一级缓存
开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,二级缓存中有数据就直接返回,没有就查一级缓存,还没有就查询数据库
Comments | NOTHING