mybatis-spring解析

1革砸、概述

原生Mybatis源碼簡析(上)
原生Mybatis源碼簡析(下)
在介紹原生Mybatis源碼簡析文章中爆阶,我們知道泛粹,Mapper接口的生命周期是在方法級別,方法執(zhí)行結(jié)束器仗,Mapper接口的動態(tài)代理實(shí)現(xiàn)類的生命就終結(jié)了。在下一次執(zhí)行時童番,會再次重新生成新的代理類精钮。但是當(dāng)mybatis與spring結(jié)合使用時,mapper接口的代理實(shí)現(xiàn)類的生命周期是全局的剃斧,這是如何實(shí)現(xiàn)的呢轨香?首先在spring中引入mybatis時,是需要加入mybatis-spring包的引用的悯衬。在spring的xml配置中需要加入如下配置:

<!-- spring和MyBatis完美整合弹沽,不需要mybatis的配置映射文件 -->  
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
        <property name="dataSource" ref="dataSource" />  
        <!-- 自動掃描mapping.xml文件 -->  
        <property name="mapperLocations" value="classpath:com/javen/mapping/*.xml"></property>  
    </bean>  
  
    <!-- DAO接口所在包名檀夹,Spring會自動查找其下的類 -->  
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">  
        <property name="basePackage" value="com.javen.dao" />  
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>  
    </bean>  

那mapper接口的代理實(shí)現(xiàn)類的單例實(shí)現(xiàn)機(jī)制,定是與這兩個bean有關(guān)了策橘。

2炸渡、SqlSessionFactoryBean

SqlSessionFactoryBean,從名字就可看出他是一個FactoryBean丽已,如此就直接看起getObject方法

public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }
public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
              "Property 'configuration' and 'configLocation' can not specified with together");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

在getObject方法中調(diào)用了afterPropertiesSet方法蚌堵,內(nèi)部又調(diào)用了buildSqlSessionFactory方法,該方法內(nèi)容很長沛婴,不再貼代碼了吼畏,但方法實(shí)現(xiàn)比較簡單,就是根據(jù)SqlSessionFactoryBean的眾多配置屬性來構(gòu)建出Configuration對象嘁灯,進(jìn)而實(shí)例化SqlSessionFactory對象泻蚊。與原廠mybatis的實(shí)現(xiàn)大同小異。

3丑婿、MapperScannerConfigurer

MapperScannerConfigurer類實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口性雄,則重點(diǎn)看下postProcessBeanDefinitionRegistry方法的實(shí)現(xiàn)

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);
    scanner.registerFilters();
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

ClassPathMapperScanner類是mybatis-spring框架提供的類,實(shí)現(xiàn)了spring框架的ClassPathBeanDefinitionScanner類羹奉。


image.png
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
        for (String basePackage : basePackages) {
            Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
            for (BeanDefinition candidate : candidates) {
                ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
                candidate.setScope(scopeMetadata.getScopeName());
                String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
                if (candidate instanceof AbstractBeanDefinition) {
                    postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                }
                if (candidate instanceof AnnotatedBeanDefinition) {
                    AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                }
                if (checkCandidate(beanName, candidate)) {
                    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                    definitionHolder =
                            AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                    beanDefinitions.add(definitionHolder);
                    registerBeanDefinition(definitionHolder, this.registry);
                }
            }
        }
        return beanDefinitions;
    }

ClassPathBeanDefinitionScanner類的doScan方法會根據(jù)傳入的basePackage秒旋,掃描包下的所有.class文件,并根據(jù)該類文件生成相應(yīng)的BeanDefinition對象诀拭。針對mybatis的mapper接口迁筛,這里生成的BeanDefinition對象其實(shí)是半成品的對象,他的beanClass屬性是接口的全限定類名耕挨。然后將半成品的BeanDefinition對象集合返回细卧,在ClassPathMapperScanner類的processBeanDefinitions方法中,對這些半成品的BeanDefinition進(jìn)行二次加工俗孝。

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()
          + "' and '" + beanClassName + "' mapperInterface");

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      definition.setBeanClass(this.mapperFactoryBeanClass);

      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

重點(diǎn)是definition.setBeanClass(this.mapperFactoryBeanClass);這段方法酒甸。

private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;

MapperFactoryBean類也是一個FactoryBean,重置了beanClass屬性之后赋铝,此時的BeanDefinition就是一個成品的了插勤,之后實(shí)例化的時候,mapper接口對應(yīng)的對象就是MapperFactoryBean的實(shí)例革骨。如此便實(shí)現(xiàn)了mapper接口的全局單例話农尖。
有空得再看看源碼分析下,spring的事務(wù)是如何與mybatis的事務(wù)整合在一起的良哲?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末盛卡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子筑凫,更是在濱河造成了極大的恐慌滑沧,老刑警劉巖并村,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滓技,居然都是意外死亡哩牍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門令漂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膝昆,“玉大人,你說我怎么就攤上這事叠必〖苑酰” “怎么了?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵纬朝,是天一觀的道長收叶。 經(jīng)常有香客問我,道長玄组,這世上最難降的妖魔是什么滔驾? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮俄讹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绕德。我一直安慰自己患膛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布耻蛇。 她就那樣靜靜地躺著踪蹬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪臣咖。 梳的紋絲不亂的頭發(fā)上跃捣,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機(jī)與錄音夺蛇,去河邊找鬼疚漆。 笑死,一個胖子當(dāng)著我的面吹牛刁赦,可吹牛的內(nèi)容都是我干的娶聘。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼甚脉,長吁一口氣:“原來是場噩夢啊……” “哼丸升!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起牺氨,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤狡耻,失蹤者是張志新(化名)和其女友劉穎墩剖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夷狰,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岭皂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了孵淘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蒲障。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瘫证,靈堂內(nèi)的尸體忽然破棺而出揉阎,到底是詐尸還是另有隱情,我是刑警寧澤背捌,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布毙籽,位于F島的核電站,受9級特大地震影響毡庆,放射性物質(zhì)發(fā)生泄漏坑赡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一么抗、第九天 我趴在偏房一處隱蔽的房頂上張望毅否。 院中可真熱鬧,春花似錦蝇刀、人聲如沸螟加。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽捆探。三九已至,卻和暖如春站粟,著一層夾襖步出監(jiān)牢的瞬間黍图,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工奴烙, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留助被,地道東北人。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓缸沃,卻偏偏與公主長得像恰起,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子趾牧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 單獨(dú)使用mybatis是有很多限制的(比如無法實(shí)現(xiàn)跨越多個session的事務(wù))检盼,而且很多業(yè)務(wù)系統(tǒng)本來就是使用sp...
    七寸知架構(gòu)閱讀 3,439評論 0 53
  • MyBatis可以獨(dú)立于Spring使用,這里我們關(guān)注一下Mybatis和Spring是怎么整合在一起的翘单。在Spr...
    半個橙子閱讀 1,579評論 0 1
  • Spring 技術(shù)筆記Day 1 預(yù)熱知識一吨枉、 基本術(shù)語Blob類型蹦渣,二進(jìn)制對象Object Graph:對象圖...
    OchardBird閱讀 965評論 0 2
  • 你是,萬圣節(jié)的小巫婆你是貌亭,不梳頭發(fā)的小瘋子你是柬唯,把家里害的一團(tuán)糟的小魔頭 你是,媽媽轉(zhuǎn)身可見的小尾巴你是圃庭,爸爸日夜...
    安妮Sweety閱讀 723評論 3 5
  • 小明和家人去餐廳吃飯锄奢。 其中點(diǎn)了一份大肉水餃。 小明問服務(wù)員:你家大肉餃子餡咋那么少? 服務(wù)員一翻白眼:昨算多剧腻,給...
    _矛盾_閱讀 119評論 0 2