在spring中使用mybatis时一般有下面的配置

    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.xuan.springmvc.mapper,org.xuan.springxxx.mapper"/>
</bean>

查看注入的MapperScannerConfigurer实现

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
......
}

发现继承了BeanDefinitionRegistryPostProcessor,

注:BeanDefinitionRegistryPostProcessor继承自BeanFactoryPostProcessor,其中有两个接口,postProcessBeanDefinitionRegistry是BeanDefinitionRegistryPostProcessor自带的,postProcessBeanFactory是从BeanFactoryPostProcessor继承过来的。postProcessBeanDefinitionRegistry是在所有Bean定义信息将要被加载,Bean实例还未创建的时候执行,优先postProcessBeanFactory执行。可以在BeanDefinitionRegistryPostProcessor的实现类中增加或者修改bean定义。

所以在初始化spring容器的时候会调用MapperScannerConfigurer#postProcessBeanDefinitionRegistry

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

跟踪ClassPathMapperScanner#scan

  @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;
}

在扫描完接口后,会配置一下接口的参数

  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
} // the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
//配置参数,在实例化mapperFactoryBean的时候传入,也是接口的类型
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
//定义BeanClass,mapperFactoryBean是一个FactoryBean,(对于FactoryBean类型的Bean,在注入对象的时候是调用的getObject创建对象)
definition.setBeanClass(this.mapperFactoryBean.getClass()); definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
} if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
} if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}

注意红色的部分,就是spring管理mybatis的关键,可以去了解FactoryBean的用法(可以简单理解为产生对象的bean,在注入这种类型的对象的时候是调用自己实现的getObject来创建的)。

跟踪MapperFactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
//intentionally empty
} public MapperFactoryBean(Class<T> mapperInterface) {
//把前面配置的对应的mapper接口传进来
this.mapperInterface = mapperInterface;
} /**
* {@inheritDoc}
*/
@Override
protected void checkDaoConfig() {
super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); //创建完成后,看是否在configuration的mapperRegistry中,没有就添加
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();
}
}
} /**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
//通过SqlSession创建实例对象
return getSqlSession().getMapper(this.mapperInterface);
} /**
* {@inheritDoc}
*/
@Override
public Class<T> getObjectType() {
//在实例化对象的时候判断配置是哪一个mapper接口
return this.mapperInterface;
} ......
}

这样就把创建接口对象又返回到mybatis中进行管理了

跟踪getSqlSession().getMapper(this.mapperInterface);最后进入了MapperRegistry#getMapper

  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);
}
}

继续MapperProxyFactory#newInstance

  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);
}

所以其实我们最后得到的对象其实是MapperProxy的代理对象。在代码中查看mapper对象

发现和分析的一直。

Mybatis的mapper接口在Spring中实例化过程的更多相关文章

  1. mybatis从mapper接口跳转到相应的xml文件的eclipse插件

    mybatis从mapper接口跳转到相应的xml文件的eclipse插件 前提条件 开发软件 eclipse 使用框架 mybatis 为了方便阅读源码,项目使用mybatis的时候,方便从mapp ...

  2. Mybatis的Mapper接口方法不能重载

    今天给项目的数据字典查询添加通用方法,发现里边已经有了一个查询所有数据字典的方法 List<Dict> selectDictList(); 但我想设置的方法是根据数据字典的code查询出所 ...

  3. 5.7 Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。-mybatis-generator将数据库表反向生成对应的实体类及基于mybatis的mapper接口和xml映射文件(类似代码生成器)

    一. liquibase 使用说明 功能概述:通过xml文件规范化维护数据库表结构及初始化数据. 1.配置不同环境下的数据库信息 (1)创建不同环境的数据库. (2)在resource/liquiba ...

  4. Mybatis的mapper接口接受的参数类型

    最近项目用到了Mybatis,学一下记下来. Mybatis的Mapper文件中的select.insert.update.delete元素中有一个parameterType属性,用于对应的mappe ...

  5. mybatis的mapper接口代理使用的三个规范

    1.什么是mapper代理接口方式? MyBatis之mapper代理方式.mapper代理使用的是JDK的动态代理策略 2.使用mapper代理方式有什么好处 使用这种方式可以不用写接口的实现类,免 ...

  6. MyBatis的Mapper接口以及Example的实例函数及详解

    来源:https://blog.csdn.net/biandous/article/details/65630783 一.mapper接口中的方法解析 mapper接口中的函数及方法 方法 功能说明 ...

  7. MyBatis 通用Mapper接口 Example的实例

    一.mapper接口中的方法解析 mapper接口中的函数及方法 方法 功能说明 int countByExample(UserExample example) thorws SQLException ...

  8. 【Mybatis】Mapper接口的参数处理过程

    下面是一个简单的Mapper接口调用,首先同个session的getMapper方法获取Mapper的代理对象,然后通过代理对象去调用Mapper接口的方法 EmployeeMapper mapper ...

  9. MyBatis绑定Mapper接口参数到Mapper映射文件sql语句参数

    一.设置paramterType 1.类型为基本类型 a.代码示例 映射文件: <select id="findShopCartInfoById" parameterType ...

随机推荐

  1. CF1032B Personalized Cup

    一个多月前写的题,既然没人写题解,那蒟蒻就写一篇吧 基本思路 既然是输出任意一个解,那么号的位置在那里是没有关系的.我的思路是默认*号在最后面,然后对输入的字符串输出的行数进行分类. 代码 #incl ...

  2. A + B for you again HDU - 1867(最大前缀&最大后缀的公共子缀&kmp删除法)

    Problem Description Generally speaking, there are a lot of problems about strings processing. Now yo ...

  3. C++ STL Vector学习 (待续)

    头文件:<vector> 初始化 vector<Elementtype> vec(); /*Elementtype是数据类型,10代表长单为10*/ vector<Ele ...

  4. HDU1401(双向BFS)

    题意:http://acm.hdu.edu.cn/showproblem.php?pid=1401 给你8*8的棋盘和4个棋子初始位置.最终位置,问你能否在8次操作后达到该状态. 思路: 双向BFS, ...

  5. 这里除了安全,什么都不会发生!Docker镜像P2P加速之路

    1.1      问题: 在使用Docker运行容器化应用时,宿主机通常先要从Registry服务(如Docker Hub)下载相应的镜像(image).这种镜像机制在开发环境中使用还是很有效的,团队 ...

  6. QAbstractItemModel使用样例与解析(Model::index使用了createIndex,它会被销毁吗?被销毁了,因为栈对象出了括号就会被销毁)

    参考:qt源码 qstandarditemmodel_p.h qstandarditemmodel.h qstandarditemmodel.cpp qabstractitemmodel.h qabs ...

  7. netty 自定义协议

    netty 自定义协议 netty 是什么呢? 相信很多人都被人问过这个问题.如果快速准确的回复这个问题呢?网络编程框架,netty可以让你快速和简单的开发出一个高性能的网络应用.netty是一个网络 ...

  8. oracle数据库(实例)的导出与导入

    Oracle数据导入导出常用两种方式: 1.是通过plsql-->tool-->export/import进行dmp文件的导入与导出: 2.使用命令imp/exp执行oracle数据导入与 ...

  9. nodejs request module里的json参数的一个坑

    今天工作的时候遇到一个坑,在客户端用nodejs给服务器发送HTTP请求,服务器老是报错:In the context of Data Services an unknown internal ser ...

  10. mysql 利用 case 批量更新