关山初度尘未洗,策马扬鞭再奋蹄!这篇文章主要讲述MapperScan的工作,Spring-Mybatis怎么自动getMapper相关的知识,希望能为你提供帮助。
@MapperScan
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
...
}
导入了MapperScannerRegistrar,这里面registerBeanDefinitions方法中new了一个ClassPathMapperScanner,调用doScan方法进行spring包扫描,会返回对应的BeanDefinition。
//ClassPathMapperScanner
@Override
public Set<
BeanDefinitionHolder>
doScan(String... basePackages) {
Set<
BeanDefinitionHolder>
beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() ->
"No MyBatis mapper was found in ‘" + Arrays.toString(basePackages) + "‘ package. Please check your configuration.");
} else {
processBeanDefinitions(beanDefinitions);
}return beanDefinitions;
}
在ClassPathMapperScanner中doScan会调用父类的doScan方法,这里面会通过BeanDefinition注册Bean
registerBeanDefinition(definitionHolder, this.registry);
在ClassPathMapperScanner的processBeanDefinitions中会设置BeanDefinition的构造参数,BeanClass等属性。
到现在为止,Spring的BeanFactory中已经有了很多MapperFactoryBean(每一个都是不同的Mapper接口类型),然后MapperFactoryBean继承了DaoSupport,在afterPropertiesSet方法中会执行checkDaoConfig();
@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
// Let abstract subclasses check their configuration.
checkDaoConfig();
// Let concrete implementations initialize themselves.
try {
initDao();
}
catch (Exception ex) {
throw new BeanInitializationException("Initialization of DAO failed", ex);
}
}
在MapperFactoryBean的checkDaoConfig方法中,会注册Mapper到Configuration的mapperRegistry中。
//MapperFactoryBean
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property ‘mapperInterface‘ is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig &
&
!configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper ‘" + this.mapperInterface + "‘ to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
然后从容器中拿Mapper的Bean的时候,实际调用MapperFactoryBean的getObject方法,会拿到对应得Mapper。
//MapperFactoryBean
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
后面就是Mybatis中的内容,会通过MapperProxyFactory new 一个MapperProxy(InnovationHandler),然后里面有MapperMethod的逻辑。
这里走Mybatis的逻辑。
//DefaultSqlSession
@Override
public <
T>
T getMapper(Class<
T>
type) {
return configuration.<
T>
getMapper(type, this);
}
//Configuration
public <
T>
T getMapper(Class<
T>
type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
//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);
}
}
MapperFactoryBean这中间还有封装,MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport中装饰了SqlSessionTemplate.(SqlSessionTemplate代理了SqlSession)
【MapperScan的工作,Spring-Mybatis怎么自动getMapper】SqlSessionTemplate装饰了SqlSessionFactory,通过SqlSessionFactory,Proxy newInstance了一个SqlSession实例(sqlSessionProxy),使用了SqlSessionInterceptor(InnovationHandler)这个代理。在这里面进行SQLSession的管理,然后在给SQLSession本身执行。
SqlSessionTemplateMybatisAutoConfiguration 注入了SqlSessionTemplate Bean在IOC容器中。
SqlSessionTemplate 实际上是Spring对于SqlSession的管理,自动创建,自动关闭。调用SqlSessionTemplate 的方法实际上是调用sqlSessionProxy[SqlSessionInterceptor为InnovationHandler]的方法。
//SqlSessionTemplate
@Override
public int delete(String statement) {
return this.sqlSessionProxy.delete(statement);
}/**
* {@inheritDoc}
*/
@Override
public int delete(String statement, Object parameter) {
return this.sqlSessionProxy.delete(statement, parameter);
}
//MybatisAutoConfiguration
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
在SqlSessionInterceptor中会自动创建sqlSession,自动关闭sqlSession。
SqlSessionInterceptor会尝试获取当前线程的sqlsession,如果没有就使用sqlsessionfactory创建。
//SqlSessionTemplate
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);
}
}
}
推荐阅读
- 小米MlX2 Android安装google play谷歌服务4套件
- android发送post请求
- android开发(多页面的实现 | Fragment的创建与使用)
- Android Intent通讯实例
- Android项目实战登录&注册
- Android项目实战欢迎界面
- 深度ghost64位win7经典装机版系统的最新下载全过程
- ghost win7深度迅速旗舰版64位系统最新下载图文详细教程
- 系统之家win7特别纯净版64位系统最新下载过程