本篇聊一聊
MyBatis
所用到的一些設計模式。
關于MyBatis一些實作原理,可以參考 MyBatis的Mapper機制
1.工場模式
關于工場模式的具體原理和實作可以參考 淺談模式 - 工場模式
SqlSessionFactory
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
SqlSession openSession(Connection connection);
SqlSession openSession(TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
上面這部分沒太多好解釋的。
純粹從設計模式角度講,就是工場方法模式,建構單例次元的
SqlSession
對象。
從MyBatis本身講,用來打開會話,如果使用了
mybatis-spring
,這部分是被屏蔽了的。參考文檔 MyBatis-Spring的Mapper機制
MapperProxyFactory
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
實際使用了
JDK
的動态代理,在調用
newInstance
方法的時候會建立一個動态代理對象
Mapper
對象。具體原理的講解可以參考 MyBatis的Mapper機制
2.建造者模式
Environment.Builder
SqlSessionFactoryBuilder
建造者模式,原理和結構都比較簡單,列出了MyBatis的實用點,具體的代碼比較大,就不列了,自己檢視即可。基本原理可以參考文檔 淺談模式 - 建造者模式
3.代理模式
關于工場模式的具體原理和實作可以參考 淺談模式 - 代理模式
非Spring的情況
在調用
sqlSession.getMapper
的時候,實時生成動态代理類(生命周期是這一次調用或者一個調用會話)。
invoke
完成之後,調用
sqlsession.close()
,看你有沒有配置連接配接池,如果配置了
connection
應該是被代理或者包裝過了。内部實際不是真正的斷開連接配接,而是放回池中。參考文檔 MyBatis的Mapper機制
Mybatis-Spring的情況
加上
spring
之後,又在
Mapper
代理的基礎上,做了
SqlSession
的代理。關鍵類
SqlSessionTemplate
。參考文檔 MyBatis-Spring的Mapper機制
4.裝飾器模式
SqlSessionTemplate
對
SqlSession
的包裝。屬于裝飾器模式。
public class SqlSessionTemplate implements SqlSession, DisposableBean {
// 包裝了SqlSession的代理對象
private final SqlSession sqlSessionProxy;
// 省去了大量代碼
// 動态代理類
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
}
5.模闆方法模式
BaseExecutor
。這是個抽象類,一般抽象類中看到下面這種抽象方法,就是用了模闆方法模式
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
其他還有一些非核心的設計模式運用。比如:
PropertyTokenizer
,運用了疊代器模式。
ErrorContext
,運用了線程級别的單例模式。