Mybatis的mapper接口在Spring中实例化过程
在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中实例化过程的更多相关文章
- mybatis从mapper接口跳转到相应的xml文件的eclipse插件
mybatis从mapper接口跳转到相应的xml文件的eclipse插件 前提条件 开发软件 eclipse 使用框架 mybatis 为了方便阅读源码,项目使用mybatis的时候,方便从mapp ...
- Mybatis的Mapper接口方法不能重载
今天给项目的数据字典查询添加通用方法,发现里边已经有了一个查询所有数据字典的方法 List<Dict> selectDictList(); 但我想设置的方法是根据数据字典的code查询出所 ...
- 5.7 Liquibase:与具体数据库独立的追踪、管理和应用数据库Scheme变化的工具。-mybatis-generator将数据库表反向生成对应的实体类及基于mybatis的mapper接口和xml映射文件(类似代码生成器)
一. liquibase 使用说明 功能概述:通过xml文件规范化维护数据库表结构及初始化数据. 1.配置不同环境下的数据库信息 (1)创建不同环境的数据库. (2)在resource/liquiba ...
- Mybatis的mapper接口接受的参数类型
最近项目用到了Mybatis,学一下记下来. Mybatis的Mapper文件中的select.insert.update.delete元素中有一个parameterType属性,用于对应的mappe ...
- mybatis的mapper接口代理使用的三个规范
1.什么是mapper代理接口方式? MyBatis之mapper代理方式.mapper代理使用的是JDK的动态代理策略 2.使用mapper代理方式有什么好处 使用这种方式可以不用写接口的实现类,免 ...
- MyBatis的Mapper接口以及Example的实例函数及详解
来源:https://blog.csdn.net/biandous/article/details/65630783 一.mapper接口中的方法解析 mapper接口中的函数及方法 方法 功能说明 ...
- MyBatis 通用Mapper接口 Example的实例
一.mapper接口中的方法解析 mapper接口中的函数及方法 方法 功能说明 int countByExample(UserExample example) thorws SQLException ...
- 【Mybatis】Mapper接口的参数处理过程
下面是一个简单的Mapper接口调用,首先同个session的getMapper方法获取Mapper的代理对象,然后通过代理对象去调用Mapper接口的方法 EmployeeMapper mapper ...
- MyBatis绑定Mapper接口参数到Mapper映射文件sql语句参数
一.设置paramterType 1.类型为基本类型 a.代码示例 映射文件: <select id="findShopCartInfoById" parameterType ...
随机推荐
- elasticsearch进行远程访问,所面对的问题解决方案
elasticsearch6.2进行远程访问,修改yml文件后,启动会报错: 上面四个问题解决方案如下: 问题1,问题2,问题3,解决如下: 注意: 针对第二个问题,你可能在limits.d目录中没有 ...
- 如何使用RedisTemplate访问Redis数据结构之Hash
Redis的Hash数据机构 Redis的散列可以让用户将多个键值对存储到一个Redis键里面. public interface HashOperations<H,HK,HV> Hash ...
- LC 387. First Unique Character in a String
题目描述 Given a string, find the first non-repeating character in it and return it's index. If it doesn ...
- JWT与Session比较和作用
1. JSON Web Token是什么 JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的.自包含的方式,用于作为JSON对象在各方之间安全地传输信息.该 ...
- 解决python在cmd运行时导入包失败,出现错误信息 "ModuleNotFoundError: No module named ***"
1.下图为我的自动化测试工程结构图 我通过运行run.bat批处理文件,调用cmd控制台运行start_run.py来开始我的自动化测试,但是出现如下错误: 大家可能知道我们的工程在IDE(Pycha ...
- 怎样快捷获取网页的window对象
使用document.defaultView; document.defaultView === window 注意: 1. 如果当前文档不属于window对象, 则返回null; 2. docume ...
- mybatis的BLOB存储与读取
http://blog.csdn.net/luyinchangdejiqing/article/details/45096689 简单介绍一下背景环境,web开发避免不了照片附件之类的东东,原先是存到 ...
- Tag Helper1
Tag Helpers是服务器段的C#代码,在Razor文件里,参与到创建和渲染HTML元素的过程 和HTML Helpers类似 跟HTML的命名规范一致 内置了很多Tag Helpers也可以自定 ...
- 【js】null 和 undefined的区别?
1.首先看一个判断题:null和undefined 是否相等 console.log(null==undefined)//true console.log(null===undefin ...
- redis 命令行操作报错
向redis集群写数据抛异常:(error) MOVED 15342 2001:fecc:0:616::34:6383 原因是启动redis-cli时未以集群方式启动,即后面要加上 -c redis- ...