前言
實際項目中通常使用mybatis-spring獲得mapper的bean對象压汪。本文通過 mybatis 和 mybatis-spring 的源碼流程 了解其實現(xiàn)方式勃痴。
mybatis-logo
mybatis doc
http://www.mybatis.org/mybatis-3/zh/getting-started.html
mybatis-spring doc
http://www.mybatis.org/spring/zh/index.html
mybatis-spring github
https://github.com/mybatis/spring
mybatis 源碼流程
session & binding
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
} finally {
session.close();
}
第一部分:構(gòu)造sessionFactory
第二部分 查詢
- sqlsession.getMapper() 返回的是接口的 動態(tài)代理 MapperProxy
- 接口調(diào)用時 MapperProxy.invoke()調(diào)用潮改,根據(jù)方法名獲取MapperMethod
- 執(zhí)行MapperMethod.execute() 根據(jù)mapper標簽區(qū)分CRUD操作烤咧,調(diào)用sqlSession。
transaction
- Transaction接口 包裝了connection, 管理其聲明周期酪耳,創(chuàng)建浓恳,提交,回滾葡兑,關(guān)閉奖蔓。
- 有兩種實現(xiàn), JDBC事務(wù) 和 Managed交由spring處理讹堤。
datasource
- POOLED 帶連接池的 數(shù)據(jù)源
PooledDataSource.getconnection() 調(diào)用popConnection()從空閑連接中獲取PooledConnection對象。
如果idleConnections非空厨疙,直接取其中的一個返回洲守。
如果activeConnections 活躍連接數(shù)小于 配置的上限,則創(chuàng)建一個新的連接返回沾凄。
如果無可用連接梗醇,等待后再次獲取。
最后 獲取將準備返回的conn記錄到activeConnections中撒蟀。
- POOLED 帶連接池的 數(shù)據(jù)源
PooledConnection是Connection的一個動態(tài)代理叙谨。主要為了在調(diào)用connection對象的接口前,檢查連接是否過期保屯。另外手负,如果調(diào)用close涤垫,會將connect歸還連接池。
- UNPOOLED 不帶連接池 的數(shù)據(jù)源
UnpooledDataSource.getconnection() 使用JDBC API 從 DriverManager獲取連接竟终。
PooledDataSource也是通過UnpooledDataSource獲取底層的連接蝠猬。
- UNPOOLED 不帶連接池 的數(shù)據(jù)源
plugin
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
- 實現(xiàn)Interceptor 接口,并通過Intercepts注解 指明需要攔截的方法统捶∮苈可以攔截 Executor ,ParameterHandler 喘鸟,ResultSetHandler 匆绣,StatementHandler 的方法。
- 解析配置文件時什黑,解析plugin標簽崎淳,生成一個攔截器的列表 InterceptorChain對象。
- 通過Configuration工廠方法 newExecutor newStatementHandler newParameterHandler newResultSetHandler 中會 通過 interceptorChain.pluginAll 應(yīng)用所有攔截器兑凿,得到代理后的對象凯力。供后續(xù)流程使用。 pluginall迭代調(diào)用所有插件的plugin方法礼华。
- Plugin提供工具方法wrap用于咐鹤,生成傳入對象的動態(tài)代理。 第一個參數(shù)為 被代理對象圣絮。第二個參數(shù)為 Interceptor (plugin)對象本身祈惶。
- 當(dāng)攔截方法調(diào)用時,會執(zhí)行Interceptor的intercept方法扮匠,傳入的Invocation 對象 封裝了代理方法的調(diào)用過程捧请,通過調(diào)用proceed方法 使之執(zhí)行。
binding
生成mapper接口的 動態(tài)代理棒搜。
實現(xiàn)方式與 retrofit 相同疹蛉。 http://www.reibang.com/p/122859d42f4f
- MapperProxyFactory<T> 用于生成mapper的動態(tài)代理對象。
- MapperProxy<T> mapper的動態(tài)代理力麸。保存了接口中的所有方法map methodCache可款。
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
接口調(diào)用時invoke根據(jù)傳入的method 在methodCache中查找方法,調(diào)用MapperMethod.execute()
- MapperMethod 根據(jù) interface method 到configuration中找到MappedStatement克蚂。根據(jù)MappedStatement的類型闺鲸,query,insert,update,delete 在execute時 將方法路由到sqlsession 相應(yīng)的實現(xiàn)上。
executor
sql的執(zhí)行器埃叭,封裝了對jdbc的調(diào)用摸恍。
sqlsession調(diào)用executor實現(xiàn) 對底層sql的執(zhí)行。
- statement 子包赤屋, 封裝了jdbc Statement 對象 的執(zhí)行立镶。StatementHandler接口壁袄。
mybatis-spring 源碼流程
- SqlSessionTemplate類
線程安全的SqlSession實現(xiàn)
使用內(nèi)部類SqlSessionInterceptor作為動態(tài)代理,invoke調(diào)用時
首先從事務(wù)管理器獲取sqlsession谜慌,如果是在一個事務(wù)中則sql是ThreadLocal中存儲的sqlsession然想。
如果不在事務(wù)中,則通過sqlSessionFactory.openSession()新創(chuàng)建一個sqlsession對象欣范。
最后調(diào)用registerSessionHolder(sqlsession) , 如果開啟事務(wù)变泄,將sqlsession注冊。
- SqlSessionTemplate類
transaction
實現(xiàn)mybatis包中的事務(wù)接口恼琼, 提供 spring 管理的 事務(wù)妨蛹。
使用了 spring-jdbc spring-tx