mybatis-spring-1.3.2.jar 中有5個類
org.mybatis.spring.mapper.MapperScannerConfigurer
org.mybatis.spring.mapper.ClassPathMapperScanner
org.mybatis.spring.mapper.MapperFactoryBean
org.mybatis.spring.SqlSessionTemplate
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor
Mapper代理對象註冊到Spring容器
我們看下MapperScannerConfigurer的定義
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
private String basePackage;
private boolean addToConfig = true;
private SqlSessionFactory sqlSessionFactory;
private SqlSessionTemplate sqlSessionTemplate;
private String sqlSessionFactoryBeanName;
private String sqlSessionTemplateBeanName;
private Class<? extends Annotation> annotationClass;
private Class<?> markerInterface;
private ApplicationContext applicationContext;
private String beanName;
private boolean processPropertyPlaceHolders;
private BeanNameGenerator nameGenerator;
// ...省略了若干方法的定義
//...
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
實現了BeanDefinitionRegistryPostProcessor接口,說明這個類在Spring容器初始化的過程中會被執行,在bean的定義被註冊後,會進入這個執行過程。
在方法postProcessBeanDefinitionRegistry
創建了一個ClassPathMapperScanner 。 而ClassPathMapperScanner會爲每個用戶定義的Mapper interface註冊一個Bean,而且這個Bean是從MapperFactoryBean#getObject來的。
雖然MapperFactoryBean本身是單例模式的(inSingleton() return true). 假設用戶定義來兩個Mapper interface。 比如StudentMapper和SecondStudentMapper兩個interface。
則MapperScannerConfigurer會添加兩個BeanDefinition,每個對應一個MapperFactoryBean.
而MapperFactoryBean實現了FactoryBean接口,所以會分別new一個MapperFactoryBean,
並通過MapperFactoryBean.geObject() 返回一個對象。比如StudentMapper對象和SecondStudentMapper對象。
MapperFactoryBean extends SqlSessionDaoSupport implements FactoryBean
創建的過程中,會設置SqlSessionDaoSupport中的一些字段。比如(SqlSessionTemplate等)
MapperFactoryBean.getObject()
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
// --> SqlSessionTemplate.java
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
//調用鏈
SqlSessionTemplate#getMapper(Class<T> type))
Configuration#mapperRegistry.getMapper(type, sqlSession);
MapperRegistry#getMapper(Class<T> type, SqlSession sqlSession)
MapperProxyFactory#newInstance()
產生一些代理類的對象。 類型爲StudentMapper。
對應的InvocationHandler是MapperProxy.java
Proxy.
至此StudentMapper的代理對象就已經生成了。並以單例到模式註冊到spirng容器
用戶調用Mapper的方法
這個實現由下面兩個類來完成
org.mybatis.spring.SqlSessionTemplate
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor
還是使用的JDK動態代理,這個就很簡單。直接貼關鍵代碼
public class SqlSessionTemplate implements SqlSession, DisposableBean {
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
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);
}
}
}
}
sqlSessionProxy是 SqlSessionInterceptor關聯的一個動態代理對象.
sqlSessionProxy的增刪改查,都會通過代理對象,這樣都會進入SqlSessionInterceptor#invoke
@Override
public <E> List<E> selectList(String statement, Object parameter) {
return this.sqlSessionProxy.<E> selectList(statement, parameter);
}
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);
...
}
sqlSession會真正根據sql語句去操作數據庫並返回結果。
SqlSession.java
package org.apache.ibatis.session;
import java.io.Closeable;
import java.sql.Connection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.BatchResult;
public interface SqlSession extends Closeable {
<T> T selectOne(String var1);
<T> T selectOne(String var1, Object var2);
<E> List<E> selectList(String var1);
<E> List<E> selectList(String var1, Object var2);
<E> List<E> selectList(String var1, Object var2, RowBounds var3);
<K, V> Map<K, V> selectMap(String var1, String var2);
...
}