@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)

SqlSessionTemplate装饰了SqlSessionFactory,通过SqlSessionFactory,Proxy newInstance了一个SqlSession实例(sqlSessionProxy),使用了SqlSessionInterceptor(InnovationHandler)这个代理。在这里面进行SQLSession的管理,然后在给SQLSession本身执行。

SqlSessionTemplate

MybatisAutoConfiguration 注入了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);
}
}
}

MapperScan的工作,Spring-Mybatis怎么自动getMapper的更多相关文章

  1. Spring mybatis源码篇章-sql mapper配置文件绑定mapper class类

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(二) 背景知识 MappedStatement是mybatis操作sql ...

  2. Spring mybatis源码篇章-MapperScannerConfigurer关联dao接口

    前言:Spring针对Mybatis的XML方式的加载MappedStatement,通过引入MapperScannerConfigurer扫描类来关联相应的dao接口以供Service层调用.承接前 ...

  3. Spring MVC+Spring+Mybatis+MySQL(IDEA)入门框架搭建

    目录 Spring MVC+Spring+Mybatis+MySQL(IDEA)入门框架搭建 0.项目准备 1.数据持久层Mybatis+MySQL 1.1 MySQL数据准备 1.2 Mybatis ...

  4. Idea SpringMVC+Spring+MyBatis+Maven调整【转】

    Idea SpringMVC+Spring+MyBatis+Maven整合   创建项目 File-New Project 选中左侧的Maven,选中右侧上方的Create from archetyp ...

  5. SpringMVC+Spring+MyBatis+Maven调整【转】

    Idea SpringMVC+Spring+MyBatis+Maven整合   创建项目 File-New Project 选中左侧的Maven,选中右侧上方的Create from archetyp ...

  6. 手把手Maven搭建SpringMVC+Spring+MyBatis框架(超级详细版)

    手把手Maven搭建SpringMVC+Spring+MyBatis框架(超级详细版) SSM(Spring+SpringMVC+Mybatis),目前较为主流的企业级架构方案.标准的MVC设计模式, ...

  7. 玩转SSH(四):Struts + Spring + MyBatis

    一.创建 SSMDemo 项目 点击菜单,选择“File -> New Project” 创建新项目.选择使用 archetype 中的 maven-webapp 模版创建. 输入对应的项目坐标 ...

  8. mybatis学习笔记(五) -- maven+spring+mybatis从零开始搭建整合详细过程(附demo和搭建过程遇到的问题解决方法)

    文章介绍结构一览 一.使用maven创建web项目 1.新建maven项目 2.修改jre版本 3.修改Project Facts,生成WebContent文件夾 4.将WebContent下的两个文 ...

  9. Spring mybatis源码篇章-MybatisDAO文件解析(二)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...

随机推荐

  1. show processlist,sysbench压力测试工具

    processlist.sh 记录数据库的状态 #!/bin/bash while true do mysql -uroot -pwangxiaohu -e 'show processlist\G'| ...

  2. 织梦dedecms 无法下载远程图片 fsockopen函数被禁用的解决方法

    在linux服务器上fsockopen()函数被无情的禁用了(这其实是出于安全考虑,可以理解),下载远程图片的功能就没有办法使用了.找了一些资料之后,找到了解决方法,就是用stream_socket_ ...

  3. 如何处理异常? catch Exception OR catch Throwable

    在Java中,当你需要统一处理异常的时候,你是会选择catch (Exception),还是直接catch (Throwable)? Java的异常体系 Throwable: Java中所有异常和错误 ...

  4. jq js 的date()使用

    Js获取当前日期时间及其它操作 var myDate = new Date();myDate.getYear(); //获取当前年份(2位)myDate.getFullYear(); //获取完整的年 ...

  5. ES提高数据压缩的设置——单字段,去掉source和all

    curl -XPUT 'http://localhost:9200/hec_test3' -d ' { "mappings": { "hec_type3": { ...

  6. js修改css时如何考虑兼容性问题es5+es6

    es5的写法 var elementStyle = document.createElement('div').style var vendor = (function(){ let transfor ...

  7. 用express实现CORS跨域(上-简单请求)

    今天遇到了一个跨域请求登录验证的问题.所以有了尝试跨域的机会. 具体情景是,有一个登录界面写在名叫cas的站点上,但是相关的登录验证的后台接口是写在名叫earth的站点. 首先的反应是使用jsonp, ...

  8. 将double型小数点后面多余的零去掉

    /** 函数功能:将数值小数点后面多余的零清空.* 参数描述:*     [in] aSource - 输入的源数值:*     [out] aDestination - 输出截取后的数值*     ...

  9. 将jsp页面内容保存到excel(转)

    在此,强调一下搜索时关键词的重要性,这样一下子可以定位到文章,否则处于盲人摸象,毫无目的尴尬境地.本篇就是通过export jsp to excel找到的. 原文地址:How to Export We ...

  10. A唐纳德先生和假骰子(华师网络赛)

    Time limit per test: 1.0 seconds Memory limit: 256 megabytes 在进行某些桌游,例如 UNO 或者麻将的时候,常常会需要随机决定从谁开始.骰子 ...