背景
Sqlsession 主要包含数据源,执行器,事务管理等。
入口
Sqlsession可以通过SqlSessionFacotry#openSessionFromDataSource 得到。
org...defaults.DefaultSqlSessionFactory#openSessionFromDataSource
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
//获取数据源环境
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);//构建sqlsession
}
创建事务工厂&数据源工厂
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
if (environment == null || environment.getTransactionFactory() == null) {
return new ManagedTransactionFactory();
}
return environment.getTransactionFactory();
}
事务工厂从environment 中取出,我们回到environment创建的入口看看(environment 对象的解析在 XMLConfigBuilder的environmentsElement 方法中),
private void environmentsElement(XNode context) throws Exception {
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
//transactionManagerElement 和 dataSourceElement 方法中根据配置的type 从 typeAliasRegistry(类别名管理器) 中拿到对应的类型
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
事务工厂类型和数据源类型别名在Configuration 初始化过程中就已经添加
public Configuration() {//2中事务工作
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
//三种数据源工厂
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
我们demo中配置的数据源type为POOLED,代表池化的数据源,也就是有线程池维护连接提高网络io效率,具体内容后面我们单独分析。
创建执行器
执行器是帮助sqlsession去和数据库交互的执行者,默认有三种执行器 batch,reuse,simple,默认用的是simple,batch主要是批处理和存储过程使用,reuse会用map缓存statment 而不会每次都创建新的。但是我们看到这三者都实现自Executor接口,而Executor的实现类却有4个,多了一个CachingExecutor,我们看看这个类的实现。
public class CachingExecutor implements Executor {
private final Executor delegate;
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
}
很显然类似包装器,就像dubbo里面wrapper,CachingExecutor实现了缓存的效果,比如从db里面query一行数据,将他缓存起来,后面根据查询条件去缓存里面取,有数据则直接返回,具体的包装逻辑在Configuration#newExecutor()中。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
Executor executor;
if (ExecutorType.BATCH == executorType) { //...
} else { executor = new SimpleExecutor(this, transaction);}
if (cacheEnabled) {//包装CachingExecutor
executor = new CachingExecutor(executor);}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
方法代码中还有一行关键代码interceptorChain.pluginAll(executor),这块代码便是我们熟悉的mybatis-plugins,平时常用的pagehelper 也是通过此功能实现的,下面我们重点看看这个拦截器的来龙去脉(这块逻辑有点绕,大家要多调试)。
interceptors 属性是在加载xml时初始化的。
#mybatis-config.xml
<plugins>
<plugin interceptor="package.Class1"></plugin>
<plugin interceptor="package.Class2"></plugin>
</plugins>
#
private void pluginElement(XNode parent) throws Exception {
for (XNode child : parent.getChildren()) {//遍历plugin 所有标签
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();//实例化插件实现类
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);//添加到configuration
}
}
org.apache.ibatis.plugin.InterceptorChain#pluginAll
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {// 遍历配置的拦截器实现对象
target = interceptor.plugin(target);
}
return target;
}
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
分析下,在进入pluginAll 方法时,传入的是CachingExecutor 对象,然后通过 Plugin.wrap() 将CachingExecutor 对象放入 jdk 动态代理的回调类 Plugin 中,返回动态代理类,此时target 已经是动态代理对象,后面再次进入循环,以此类推,debug 结果如下。
可以看到 object 经历了2层的jdk动态代理,最后返回的是一个代理对象
$Proxy3@22187,我们用框图的形式再次展示下对象组装结构。
执行器执行流程
我们模拟下调用链路,调用的过程属于责任链模式,顺序应该比较明确就是写在前面的先添加,后执行。
sequenceDiagram
DefaultSqlSession->>$Executor2: query()
$Executor2->>$Executor2: invoke()
$Executor2->>MyPlugin2: intercept()
MyPlugin2->>$Executor1: query()
$Executor1->>$Executor1: invoke()
$Executor1->>MyPlugin1: intercept()
MyPlugin1->>CachingExecutor: query()
CachingExecutor->>db: query()
到这里就分析完了。