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

前言

  开心一刻   html

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

前情回顾

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

  可是,此时SqlSessionFactory的建立、SqlSession的建立以及mapper代理对象的获取都是咱们手动操做的,实际应用中,mybatis每每也不会单独使用,绝大多数都是集成在spring中,也就是说咱们无需手动去管理mybatis相关对象的生命周期,所有都由spring容器统一管理,那么spring是何时在哪建立的mybatis的相关对象的呢?尤为是mapper代理对象MapperProxy的建立数据库

Springboot集成mybatis

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

  自动配置:MybatisAutoConfiguration

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

    MybatisAutoConfiguration会被当作配置类被spring解析,咱们来看看spring容器会今后配置类中解析到什么app

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

  @Import({ AutoConfiguredMapperScannerRegistrar.class })

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

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

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

  ClassPathMapperScanner scanner = new ClassPathMapperScanner(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

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

  mapper代理对象的建立:MapperFactoryBean

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

public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
}

public void setSqlSessionTemplate(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类型
            return createBean(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);
            throw ex;
        }
    });
    // 获取给定bean实例的对象,若是是FactoryBean,则获取bean实例自己或其建立的对象
    bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
View Code  

    由于Spring在mapper扫描的时候,将全部mapper bean定义中的beanClass设置成了MapperFactoryBean(继承了FactoryBean),因此经过createBean方法建立的mapper实例其实是MapperFactoryBean对象,而后经过

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

protected Object 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 instanceof NullBean) {
            return beanInstance;
        }
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(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)) {
        return beanInstance;
    }

    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);
    }
    return object;
}
View Code

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

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

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

总结

  一、自动配置的过程当中,spring会扫描全部的mapper,并将全部mapper bean定义中的beanClass指向MapperFactoryBean;

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

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

  四、关于spring的FactoryBean,有时间再从新开一篇给你们讲讲

相关文章
相关标签/搜索