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


前言

  開心一刻   

    中韓兩學生辯論。
    中:端午節是屬於誰的?
    韓:韓國人!
    中:漢字是誰發明的?
    韓:韓國人!
    中:中醫是屬於誰的?
    韓:韓國人!
    中:那中國人到底發明過什么?
    韓:韓國人!

前情回顧

  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

    Mybatis的自動配置類:MybatisAutoConfiguration,至於如何加載此類,可參考:spring-boot-2.0.3啟動源碼篇一 - SpringApplication構造方法

    MybatisAutoConfiguration會被當做配置類被spring解析,我們來看看spring容器會從此配置類中解析到什么

     創建了SqlSessionFactory實例(實際類型:DefaultSqlSessionFactory),並注冊到了spring容器;此時我們應該還注意到

  @Import({ AutoConfiguredMapperScannerRegistrar.class })

    AutoConfiguredMapperScannerRegistrar繼承了ImportBeanDefinitionRegistrar(注意看類注釋,有興許的可以更深入的研究下),那么它的registerBeanDefinitions也會被調用

@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全部指向了MapperFactoryBean

  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代理對象的生成,你有想過嗎

  至此,前情回顧中的問題也就清晰了

總結

  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,我們來看看其源碼實現


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM