一. 测试代码

//实体类
public class User {
private Integer id;
private String name;
private Integer age;
private String email;
private SexEnum sex;
//getter / setter
} public enum SexEnum { Male("Male", "男"), Female("Female", "女"); private String code; private String desc; SexEnum(String code, String desc){
this.code = code;
this.desc = desc;
}
} //Repository
@Repository
public interface UserMapper { public User getById(Integer id); public List<User> getByAge(Integer age); public int insert(User user);
} @SpringBootApplication
@MapperScan("com.study.demo.mybatis.mapper")
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class, args);
}
} @Test
public void getById(){
System.out.println("getById 开始执行...");
User user = userMapper.getById(1);
System.out.println(user);
System.out.println("getById 结束执行...");
}

mapper.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.demo.mybatis.mapper.UserMapper">
<insert id="insert">
insert into user (name,age,email,sex) values(#{name}, #{age}, #{email}, #{sex})
</insert> <select id="getById" resultType="com.study.demo.mybatis.vo.User">
select * from user where id = #{id}
</select> <select id="getByAge" resultType="com.study.demo.mybatis.vo.User">
select * from user where age = #{age}
</select>
</mapper>

配置文件:

spring:
datasource:
#type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/deco?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false
username: root
password: 123456 mybatis:
mapper-locations: classpath:mapper/**Mapper.xml

@MapperScan("com.study.demo.mybatis.mapper") 主要做了两件事情:

1. 根据 "com.study.demo.mybatis.mapper" 配置进行mapper.java文件扫描.

  此处扫描到的就是 SchoolMapper.java 和 UserMapper.java 两个文件

2. 为扫描到的文件进行 BeanDefinition 注册

//关键代码: 
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
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
definition.setBeanClass(this.mapperFactoryBean.getClass());
......
}

在进入此方法之前,  beanDefinitions 已经通过扫描得到,

beanName beanClass
schoolMapper com.study.demo.mybatis.mapper.SchoolMapper
userMapper com.study.demo.mybatis.mapper.UserMapper

进入方法后, 会执行以下代码, 对 beanClass 进行重新赋值:

definition.setBeanClass(this.mapperFactoryBean.getClass());

这里的 mapperFactoryBean 是 ClassPathMapperScanner 的一个私有属性:

private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();

也就是说, 后面对 beanName = userMapper 进行创建的时候, 会使用到  MapperFactoryBean

二. 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) {
this.mapperInterface = mapperInterface;
} /**
* {@inheritDoc}
*/
@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();
}
}
} /**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
} /**
* {@inheritDoc}
*/
@Override
public Class<T> getObjectType() {
return this.mapperInterface;
} /**
* {@inheritDoc}
*/
@Override
public boolean isSingleton() {
return true;
} //------------- mutators -------------- /**
* Sets the mapper interface of the MyBatis mapper
*
* @param mapperInterface class of the interface
*/
public void setMapperInterface(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
} /**
* Return the mapper interface of the MyBatis mapper
*
* @return class of the interface
*/
public Class<T> getMapperInterface() {
return mapperInterface;
} /**
* If addToConfig is false the mapper will not be added to MyBatis. This means
* it must have been included in mybatis-config.xml.
* <p/>
* If it is true, the mapper will be added to MyBatis in the case it is not already
* registered.
* <p/>
* By default addToCofig is true.
*
* @param addToConfig
*/
public void setAddToConfig(boolean addToConfig) {
this.addToConfig = addToConfig;
} /**
* Return the flag for addition into MyBatis config.
*
* @return true if the mapper will be added to MyBatis in the case it is not already
* registered.
*/
public boolean isAddToConfig() {
return addToConfig;
}
}

从代码上看, 实现了 FactoryBean 接口,  他是一个 工厂bean.

在创建 userMapper 的时候, 就会调用 MapperFactoryBean 的 getObject() 方法.

md版本: https://files.cnblogs.com/files/elvinle/mybatis.zip

mybatis - @MapperScan的更多相关文章

  1. Mybatis——@MapperScan原理

    @MapperScan配置在@Configuration注解的类上会导入MapperScannerRegistrar类. 而MapperScannerRegistrar实现了ImportBeanDef ...

  2. SpringBoot 使用yml配置 mybatis+pagehelper+druid+freemarker实例

    SpringBoot 使用yml配置 mybatis+pagehelper+druid+freemarker实例 这是一个简单的SpringBoot整合实例 这里是项目的结构目录 首先是pom.xml ...

  3. Mybatis日志源码探究

    一.项目搭建 1.pom.xml <dependencies> <dependency> <groupId>log4j</groupId> <ar ...

  4. springboot和mybatis集成

    springboot和mybatis集成 pom  <?xml version="1.0" encoding="UTF-8"?> <proje ...

  5. springboot与缓存(redis,或者caffeine,guava)

    1.理论介绍 Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry. CachingProvide ...

  6. SpringBoot与Shiro的整合(单体式项目)

    1.包结构 2.jar包,配置文件,类 2.1pom.xml文件配置 <?xml version="1.0" encoding="UTF-8"?> ...

  7. Spring IOC Container原理解析

    Spring Framework 之 IOC IOC.DI基础概念 关于IOC和DI大家都不陌生,我们直接上martin fowler的原文,里面已经有DI的例子和spring的使用示例 <In ...

  8. Spring Boot MyBatis注解:@MapperScan和@Mapper

    最近参与公司的新项目架构搭建,在使用mybatis的注解时,和同时有了不同意见,同事认为使用@Mapper注解简单明了,而我建议使用@MapperScan,直接将mapper所在的目录扫描进去就行,而 ...

  9. 启动类加注解@MapperScan spring boot mybatis 启动错误

    Description: Field userDao in com.gcy.springsecuritydemo.service.user.UserService required a bean of ...

随机推荐

  1. [51nod 1256] 乘法逆元 - exgcd

    给出2个数M和N(M < N),且M与N互质,找出一个数K满足0 < K < N且K * M % N = 1,如果有多个满足条件的,输出最小的. Solution 用 EXGCD 求 ...

  2. VSCode添加git bash作为默认终端

    VSC添加git bash作为默认终端的settings.json添加 { "terminal.integrated.shell.windows": "D:\\Progr ...

  3. vue自定义插件

    1.新建js文件 utils.js,自定义方法 let local = { say() { console.log('我是插件里面自定义的方法') } } export default { insta ...

  4. ng-做一个简单的通讯录--学习使用路由和HTTP

    app.module import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@ang ...

  5. pandas学习笔记之删除指定列

    删除指定 def df["列名"] del df4["韩国地震影响"] 直接删除,df4中不在含有"韩国地震影响"这一列了 drop 不改变 ...

  6. [Java]对double变量进行四舍五入,并保留小数点后位数

    1.功能 将double类型变量进行四舍五入,并保留小数点后位数 2.代码 import java.math.BigDecimal; import java.math.RoundingMode; im ...

  7. mysql 基础sql语法总结(一)DDL

    mysql数据库: SQL数据库语言可分为四部分: 1.DDL:对数据库或表的进行操作结构操作 2.DML:对表的记录进行更新(增.删.改)* 3.DQL:对表的内容进行查询 **(重难点) 4.DC ...

  8. pandas玩转excel-> (2)如何利用pandas读取excel数据文件

    import pandas as pd #将excel文件读到内存中,形成dataframe,并命名为peoplepeople=pd.read_excel('D:/python结果/task2/Peo ...

  9. JS 百度地图路书---动态路线

    JS 百度地图路书---动态路线 <!DOCTYPE html> <head> <meta http-equiv="Content-Type" con ...

  10. git配置多仓库

    git配置多仓库 github , gitee , coding , gitlab , gitlab.company ..... 真TM多 . 真TM多 . 真TM多 . 生成ssh 生成ssh 密钥 ...