首頁 > 軟體

Spring和Mybatis整合的原理詳解

2022-07-05 18:05:53

前言

最近讀完了Spring的IOC部分的原始碼,受益匪淺,這篇文章講解一下MyBatis是如何做到與Spring整合的。MyBatis是如何做到干擾Spring的生命週期,把Mapper一個個的註冊到Spring容器中的將在這裡揭祕。

簡單猜想

因為閱讀過Spring原始碼後對他有了一定的認識,這裡可以簡單盲猜一下,使用的是什麼方式,在上一篇文章揭祕Autowired註解中有介紹到。我們只是向xml中寫入了一行<context:annotation-config/>設定。Spring就像BeanFatory中寫入了很多的BeanPostProcessor,這裡我覺得采用的功能類似。
通過定義spring.handlers檔案。然後mybatis定義各種處理標籤的Handler和Paser。隨後通過讀取組態檔中的mappers標籤,去像register中註冊BeanDefinition,這是其一。

第二種方法就是類似AutowiredAnnotationBeanPostProcessor的實現方式,通過Xml註冊一個Bean,這個Bean繼承自MergedBeanDefinitionPostProcessor,由於繼承自MegedBeanDefinitionPostProcessor所以他會優先Bean執行,在此時可以像Bean工廠中新增BeanDefinition。

其實我們的目標很明確,只要我們能在Spring呼叫InitiazionBean方法之前去把mapper的BeanDefinition新增進Spring容器,都可以實現當前的目的。

那麼接下來我們就看看MyBatis本身究竟是如何實現的吧。

案例搭建

原始碼地址:MyBatis整合Spring有兩種方式,第一種是通過Xml,第二種是通過Mapper介面的掃描,具體的整合方法我這裡就不演示了,直接看組態檔吧。其實就是application.xml有一點改動。

通過掃描介面

這裡搭建一個最簡單的整合方式:

正式開始

通過上方的組態檔,可以看見一個設定了一個叫scannerConfigurer,這裡先去看一下這個類。

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {

這個類繼承了幾個介面:

  • BeanDefinitionRegistryPostProcessor
  • 這個介面追進去,發現該介面繼承自BeanFactoryPostProcessor,也就相當於spring在refresh方法中有一個方法專門去執行這類的介面。
  • InitializingBean
  • 在createBean的生命週期中會呼叫該介面的afterProperties方法。
  • ApplicationContextAware
  • Spring在建立該Bean時會呼叫setapplicationContext方法注入上下文
  • BeanNameAware
  • 建立是呼叫setBeanName方法

按照這樣的一個介面被執行的順序是,setBeanName -> setApplicationContext -> afterproperties -> postProcessBeanDefinitionRegistry

setBeanName

@Override
public void setBeanName(String name) {
  this.beanName = name;
}

這個方法很明顯不是。

setApplicationContext

@Override
public void setApplicationContext(ApplicationContext applicationContext) {
  this.applicationContext = applicationContext;
}

這個方法很明顯,也不是。

afterProperties

@Override
public void afterPropertiesSet() throws Exception {
  notNull(this.basePackage, "Property 'basePackage' is required");
}

這個方法是用來校驗的,判斷了basePackage是否為空,如果為空就throw Exception。

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.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
  if (StringUtils.hasText(lazyInitialization)) {
    scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
  }
  if (StringUtils.hasText(defaultScope)) {
    scanner.setDefaultScope(defaultScope);
  }
  scanner.registerFilters();
  scanner.scan(
      StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

那這裡就很明顯了,這裡建立ClassPathMapperScanner。隨後對scanner的一些設定做了一些設定。

然後就呼叫了registerFilters方法,字面意思也就是註冊過濾器,這裡就跳過吧,無非是設定一些屬性,然後在後面解析的時候判斷過濾條件,在迴圈時continue。

主要是scan方法這裡要詳細看一下:

public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    this.doScan(basePackages);
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}

這裡這個掃描類其實是Spring中的類,ClassPathBeanDefinitionScanner,當中的scan方法。所以這裡可能會有點眼熟,類似於建立Bean時,先獲取一下建立之前的Bean總數,然後再獲取建立之後的Bean總數,返回時減一下就知道這次建立了多少。

總結

其實到這裡呢,我們就算是結束了,因為後續的包掃描,在嚴格意義上來講是Spring來實現的,我後續開篇文章來講解這個東西。

這裡總結一下,正如我猜想的一樣,myBatis只要在finishBeanFactoryInitialization方法之前,把Mapper的BeanDefinition塞進Spring容器中,在最後的finishBeanFactoryInitialization方法,Spring自然就會根據BeanDefinition去建立Bean了。

這裡使用的方法是,註冊一個BeanFactoryPostProcessor,所以這個方法會在finishBeanFactoryInitialization方法之前執行,所以這裡是成功的。

到此這篇關於Spring和Mybatis整合的原理詳解的文章就介紹到這了,更多相關Spring和Mybatis整合內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com