springboot集成下,mybatis的mapper代理对象究竟是如何生成的

摘要:
尤其是mapper代理对象MapperProxy的创建Springboot集成mybatis当springboot集成mybatis后,mybatis的对象是交给spring容器管理的,只会实例化一次,然后伴随着spring容器一直存在,直到spring容器销毁  自动配置:MybatisAutoConfigurationMybatis的自动配置类:MybatisAutoConfiguration,至于如何加载此类,可参考:spring-boot-2.0.3启动源码篇一-SpringApplication构造方法MybatisAutoConfiguration会被当做配置类被spring解析,我们来看看spring容器会从此配置类中解析到什么创建了SqlSessionFactory实例,并注册到了spring容器;此时我们应该还注意到@ImportAutoConfiguredMapperScannerRegistrar继承了ImportBeanDefinitionRegistrar,那么它的registerBeanDefinitions也会被调用@OverridepublicvoidregisterBeanDefinitions{logger.debug;ClassPathMapperScannerscanner=newClassPathMapperScanner;try{if(this.resourceLoader!

前言

开心一刻   

中韩两学生辩论。
中:端午节是属于谁的?
韩:韩国人!
中:汉字是谁发明的?
韩:韩国人!
中:中医是属于谁的?
韩:韩国人!
中:那中国人到底发明过什么?
韩:韩国人!

springboot集成下,mybatis的mapper代理对象究竟是如何生成的第1张

前情回顾

Mybatis源码解析 - mapper代理对象的生成,你有想过吗,我们讲到了mybatis操作数据库的流程:先创建SqlSessionFactory,然后创建SqlSession,然后再创建获取mapper代理对象,最后利用mapper代理对象完成数据库的操作;Mapper代理对象的创建,利用的是JDK的动态代理,InvocationHandler是MapperProxy,后续Mapper代理对象方法的执行都会先经过MapperProxy的invoke方法。

但是,此时SqlSessionFactory的创建、SqlSession的创建以及mapper代理对象的获取都是我们手动操作的,实际应用中,mybatis往往也不会单独使用,绝大多数都是集成在spring中,也就是说我们无需手动去管理mybatis相关对象的生命周期,全部都由spring容器统一管理,那么spring是什么时候在哪创建的mybatis的相关对象的呢?尤其是mapper代理对象MapperProxy的创建

Springboot集成mybatis

当springboot(其实还是spring)集成mybatis后,mybatis的对象是交给spring容器管理的,只会实例化一次,然后伴随着spring容器一直存在,直到spring容器销毁

  自动配置:MybatisAutoConfiguration

springboot集成下,mybatis的mapper代理对象究竟是如何生成的第2张

Mybatis的自动配置类:MybatisAutoConfiguration,至于如何加载此类,可参考:spring-boot-2.0.3启动源码篇一 - SpringApplication构造方法

MybatisAutoConfiguration会被当做配置类被spring解析,我们来看看spring容器会从此配置类中解析到什么

springboot集成下,mybatis的mapper代理对象究竟是如何生成的第3张

创建了SqlSessionFactory实例(实际类型:DefaultSqlSessionFactory),并注册到了spring容器;此时我们应该还注意到

  @Import({ AutoConfiguredMapperScannerRegistrar.class })

AutoConfiguredMapperScannerRegistrar继承了ImportBeanDefinitionRegistrar(注意看类注释,有兴许的可以更深入的研究下),那么它的registerBeanDefinitions也会被调用

@Override
public voidregisterBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

  logger.debug("Searching for mappers annotated with @Mapper");

  ClassPathMapperScanner scanner = newClassPathMapperScanner(registry);

  try{
    if (this.resourceLoader != null) {
      scanner.setResourceLoader(this.resourceLoader);
    }

    //获取启动类所在的包,如:com.lee.shiro,会作为扫描开始的base package,一般只会有一个,但支持多个
    List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
    if(logger.isDebugEnabled()) {
      for(String pkg : packages) {
        logger.debug("Using auto-configuration base package '{}'", pkg);
      }
    }

    scanner.setAnnotationClass(Mapper.class);                    //设置扫谁,Mapper注解是被扫描对象
scanner.registerFilters();
    scanner.doScan(StringUtils.toStringArray(packages));        //扫描所有mapper,进行bean定义处理
  } catch(IllegalStateException ex) {
    logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
  }
}
View Code

springboot集成下,mybatis的mapper代理对象究竟是如何生成的第4张

以我们启动类所在的包(com.lee.shiro)为基包,扫描所有的mapper,然后修改所有mapper在spring容器中的bean定义,将mapper的beanClass全部指向了MapperFactoryBean

  mapper代理对象的创建:MapperFactoryBean

MapperFactoryBean继承SqlSessionDaoSupport,SqlSessionDaoSupport有两个方法用来设置SqlSession

public voidsetSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = newSqlSessionTemplate(sqlSessionFactory);
    }
}

public voidsetSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    this.sqlSession =sqlSessionTemplate;
    this.externalSqlSession = true;
}
View Code

可以看到SqlSession的实际类型是:SqlSessionTemplate,SqlSessionTemplate在MybatisAutoConfiguration以@Bean方式创建的

Spring在创建Service实例:UserServiceImpl的时候,发现依赖mapper(可能还有其他的实例依赖mapper),那么就会去spring容器获取mapper实例,没有则进行创建,然后注入进来(依赖注入);具体创建过程如下

if(mbd.isSingleton()) {
    sharedInstance = getSingleton(beanName, () ->{
        try{
            //创建mapper对象,beanName:com.lee.shiro.mapper.UserMapper,创建出来的实例实际上是MapperFactoryBean类型
            returncreateBean(beanName, mbd, args);        
        }
        catch(BeansException ex) {
            //Explicitly remove instance from singleton cache: It might have been put there
            //eagerly by the creation process, to allow for circular reference resolution.
            //Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
            throwex;
        }
    });
    //获取给定bean实例的对象,如果是FactoryBean,则获取bean实例本身或其创建的对象
    bean =getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
View Code  

springboot集成下,mybatis的mapper代理对象究竟是如何生成的第5张

因为Spring在mapper扫描的时候,将所有mapper bean定义中的beanClass设置成了MapperFactoryBean(继承了FactoryBean),所以通过createBean方法创建的mapper实例实际上是MapperFactoryBean对象,然后通过

bean =getObjectForBeanInstance(sharedInstance, name, beanName, mbd);

protectedObject getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

    //Don't let calling code try to dereference the factory if the bean isn't a factory.
    //isFactoryDereference方法判断name中是否有&字符
    if(BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceofNullBean) {
            returnbeanInstance;
        }
        if (!(beanInstance instanceofFactoryBean)) {
            throw newBeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
        }
    }

    //Now we have the bean instance, which may be a normal bean or a FactoryBean.
    //If it's a FactoryBean, we use it to create a bean instance, unless the
    //caller actually wants a reference to the factory.
    //此时的beanInstance可能是一个普通bean,也可能是一个FactoryBean
    //如果是一个FactoryBean,那么就用它创建想要的bean实例
    //此if表示,如果beanInstance是普通bean,或者本来就想要FactoryBean实例,则直接返回beanInstance
    if (!(beanInstance instanceof FactoryBean) ||BeanFactoryUtils.isFactoryDereference(name)) {
        returnbeanInstance;
    }

    Object object = null;
    if (mbd == null) {
        object =getCachedObjectForFactoryBean(beanName);
    }
    //此时表明beanInstance是一个FactoryBean,并且不是想要FactoryBean实例
    if (object == null) {
        //Return bean instance from factory.
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        //Caches object obtained from FactoryBean if it is a singleton.
        if (mbd == null &&containsBeanDefinition(beanName)) {
            mbd =getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null &&mbd.isSynthetic());
        //通过FactoryBean实例创建我们想要的实例
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    returnobject;
}
View Code

获取真正想要的bean实例,如果beanInstance是普通bean,或者本来就想要FactoryBean实例(beanName中有&),那么直接返回beanInstance,否则用FactoryBean实例来创建我们想要的实例对象。说回来就是会调用MapperFactoryBean的getObject()方法来获取Mapper的代理对象

后续流程就可以参考:Mybatis源码解析 - mapper代理对象的生成,你有想过吗

至此,前情回顾中的问题也就清晰了

总结

1、自动配置的过程中,spring会扫描所有的mapper,并将所有mapper bean定义中的beanClass指向MapperFactoryBean;

2、创建mapper实例的时候,根据bean定义创建的实例实际上是MapperFactoryBean实例,然后再利用MapperFactoryBean获取mapper实例(调用MapperFactoryBean的getObject方法,mybatis会利用jdk的动态代理创建mapper代理对象);

3、对比Mybatis源码解析 - mapper代理对象的生成,你有想过吗,其实就是将我们手动创建的过程通过自动配置,将创建过程交给了spring;

4、关于spring的FactoryBean,请看看这篇:Spring拓展接口之FactoryBean,我们来看看其源码实现

免责声明:文章转载自《springboot集成下,mybatis的mapper代理对象究竟是如何生成的》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇EasyNVR无插件直播服务如何配合EasyBMS使用以及实现流媒体管理功能概述自定义线程池实现下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MyBatis + PageHelper

1. 引入分页插件 在 pom.xml 中添加如下依赖: <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>最新版...

Spring Boot -- Spring Boot之@Async异步调用、Mybatis、事务管理等

这一节将在上一节的基础上,继续深入学习Spring Boot相关知识,其中主要包括@Async异步调用,@Value自定义参数、Mybatis、事务管理等。 本节所使用的代码是在上一节项目代码中,继续追加的,因此需要先学习上一节内容。 一、使用@Async实现异步调用 要在springboot中使用异步调用方法,只要在被调用的方法上面加上@Async就可以...

Jsp基础知识

Jsp:Java Server Pages http://127.0.0.1:8080/weba/hello.html Tomcat执行过程: 浏览器通过http协议发送请求,以TCP协议作为底层,去tomcat的安装目录下找到webapps下的weba文件夹,再继续找到hello.html. http协议有协议头和协议头,底层是TCP,是无状态的,两次连...

No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: more than one 'primary' bean found among candidates:

完整的异常提示信息: No qualifying bean of type 'org.springframework.transaction.TransactionManager' available: more than one 'primary'bean found among candidates: [dataBaseOneTransactionMa...

使用IDEA工具整合mybatis时使用@Resource和@Autowired自动注解bean时会显示红色问题的解决办法

使用IDEA工具整合mybatis时使用@Resource和@Autowired自动注解bean时会显示红色问题的解决办法 idea中springboot整合mybatis时,通过@Autowired注入的对象一直有下划线提示,但是项目能运行,虽然不影响运行,但是强迫症的程序员肯定看不下去. 如何去除呢?解决:改变@Autowired的检查级别即可.快捷键...

Mybatis框架(9)---Mybatis自定义插件生成雪花ID做为表主键项目

Mybatis自定义插件生成雪花ID做为主键项目 先附上项目项目GitHub地址spring-boot-mybatis-interceptor 有关Mybatis雪花ID主键插件前面写了两篇博客作为该项目落地的铺垫。 1、Mybatis框架---Mybatis插件原理 2、java算法---静态内部类实现雪花算法 该插件项目可以直接运用于实际开发中,作为...