最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis-Spring源碼分析圖解

 更新時(shí)間:2021年11月17日 14:43:21   作者:Jame!  
這篇文章主要介紹了Mybatis-Spring源碼分析,本文通過實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

Mybatis-Spring

當(dāng)我們使用mybatis和spring整合后為什么下面的代碼可以運(yùn)行?

一個(gè)問題:

我就寫了個(gè)mapper接口為什么能用?

首先來看,在spring的配置xml中有一段

<bean id="configurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    <property name="basePackage" value="com.jame.dao"/>
</bean>

這段xml的作用是將一個(gè)類添加到spring容器中,點(diǎn)進(jìn)這個(gè)類看看

它實(shí)現(xiàn)了一個(gè)BeanDefinitionRegistryPostProcessor接口,關(guān)于這個(gè)接口的作用和執(zhí)行時(shí)機(jī)上篇博客寫過了,這里就不再贅述

那么它必然實(shí)現(xiàn)postProcessBeanDefinitionRegistry方法,點(diǎn)擊這個(gè)方法查看

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
        processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    ..........
    scanner.registerFilters();
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

其中將接口注冊(cè)到spring容器中在最后一行,先來看ClassPathMapperScanner這個(gè)類,它繼承了ClassPathBeanDefinitionScanner這個(gè)掃描器

scan的具體代碼

public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

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

這個(gè)是spring內(nèi)部的掃描方法,當(dāng)它走到doScan的時(shí)候,因?yàn)镃lassPathMapperScanner這個(gè)類重寫了doScan方法,所以會(huì)調(diào)用子類重寫的方法

@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
        LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
                    + "' package. Please check your configuration.");
    } else {
        processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}

通過包名獲取BeanDefinitionHolder,現(xiàn)在它獲取到了User接口的BeanDefinitionHolder,然后判斷如果BeanDefinitionHolder的集合為空,也就是沒有找到mapper的情況則不做任何處理,而現(xiàn)在有一個(gè)UserMapper的,進(jìn)入else

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    AbstractBeanDefinition definition;
    BeanDefinitionRegistry registry = getRegistry();
    for (BeanDefinitionHolder holder : beanDefinitions) {
        definition = (AbstractBeanDefinition) holder.getBeanDefinition();
       	.........
        //主要看這行
        definition.setBeanClass(this.mapperFactoryBeanClass);
	  .........
        if (!definition.isSingleton()) {
            BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
            if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
                registry.removeBeanDefinition(proxyHolder.getBeanName());
            }
            registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
        }
    }
}

將MapperFactoryBean類設(shè)置為了UserMapperBeanDefinition的class

spring在創(chuàng)建這個(gè)userMapper這個(gè)Bean的時(shí)候會(huì)使用這個(gè)有參構(gòu)造將當(dāng)前這個(gè)UserMapper類型設(shè)置到mapperInterface屬性上(為啥使用有參構(gòu)造而不是無參來初始化對(duì)象我也不知道.....這和spring推斷構(gòu)造方法有關(guān),以后學(xué)會(huì)了在來寫)

這個(gè)MapperFactoryBean實(shí)現(xiàn)了一個(gè)FactoryBean接口,這個(gè)接口可以讓我們自定義獲取bean的操作

回到spring的代碼,例如當(dāng)我們使用context.getBean(xxx.class)的時(shí)候

spring將xxx.class類型解析為bean名稱,通過名稱去獲取

protected <T> T doGetBean(
    String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
    throws BeansException {
    //獲取對(duì)應(yīng)的beanName
    String beanName = transformedBeanName(name);
    Object bean;
    Object sharedInstance = getSingleton(beanName);

    if (sharedInstance != null && args == null) {
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
	.......
    // Create bean instance.
    if (mbd.isSingleton()) {
        sharedInstance = getSingleton(beanName, () -> {
            try {
                //真正創(chuàng)建對(duì)象的地方
                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 = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
    }     
}

首先是調(diào)用getSingleton方法,嘗試獲取存在緩存中的bean(其實(shí)就是三個(gè)Map,key為bean名稱,value是對(duì)象),那現(xiàn)在是首次獲取map中沒有

然后執(zhí)行到下面的createBean,當(dāng)創(chuàng)建完這個(gè)bean后spring需要判斷這個(gè)bean是一個(gè)普通bean還是一個(gè)FactoryBean,程序員是想要獲取普通bean還是FactoryBean,還是FactoryBean的getObject方法返回的從工廠生成的對(duì)象

咱們一段一段看

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

    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
        }
    }
	.....
}

BeanFactoryUtils.isFactoryDereference(name)的作用是一個(gè)字符串判斷,當(dāng)返回傳入名稱是否為工廠,如果name不為空,并且以&開頭返回true

這個(gè)方法在下面的判斷也使用到了,記一下它的作用即可

來看例子

在我們使用FactoryBean通過context.getBean("工廠Bean名稱")的時(shí)候獲取的是FactoryBean的getObject生成的對(duì)象,如果我們想獲取FactoryBean的引用則需要在名稱前面加一個(gè)&符號(hào)

回來看代碼,如果這個(gè)bean的引用是一個(gè)NullBean類型則直接返回引用,下面有做了一個(gè)判斷

if (!(beanInstance instanceof FactoryBean))再次判斷這個(gè)bean是不是一個(gè)FactoryBean,如果為true則拋出異常,這個(gè)好理解,因?yàn)槲覀冊(cè)趃etBean的時(shí)候完全可以將一個(gè)普通的bean名稱前面加上&符號(hào)

主要的判斷在下面的這個(gè)if

if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
    return beanInstance;
}

現(xiàn)在有3中情況

1.當(dāng)前的bean是一個(gè)普通的bean

第一個(gè)條件false 取反 true 第二個(gè)條件false 結(jié)果true,直接返回bean實(shí)例

2.當(dāng)前是一個(gè)FactoryBean,想通過工廠獲取Bean

第一個(gè)條件 true 取反false 第二個(gè)條件false 結(jié)果false,進(jìn)行下面的操作

3.當(dāng)前是一個(gè)FactoryBean,想獲取工廠的引用

第一個(gè)條件 true 取反 false 第二個(gè)條件 true 結(jié)果 true 直接返回factoryBean實(shí)例

當(dāng)前我們是想通過FactoryBean獲取對(duì)象,那么不進(jìn)if,繼續(xù)下面的代碼

Object object = null;
// 如果beanDefinition為null,則嘗試從緩存中獲取給定的FactoryBean公開的對(duì)象
if (mbd == null) {
    //嘗試從緩存中加載bean
    object = getCachedObjectForFactoryBean(beanName);
}
// 未能從緩存中獲得FactoryBean公開的對(duì)象,則說明該bean是一個(gè)新創(chuàng)建的bean
if (object == null) {
    FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
    if (mbd == null && containsBeanDefinition(beanName)) {
        mbd = getMergedLocalBeanDefinition(beanName);
    }
    boolean synthetic = (mbd != null && mbd.isSynthetic());
    // 從給定的FactoryBean中獲取指定的beanName對(duì)象
    object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;

主要來看getObjectFromFactoryBean

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                //調(diào)用factoryBean的getObject方法
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
            }
            ..........
        }
    }
}

doGetObjectFromFactoryBean方法

private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
    Object object;
    try {
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            //調(diào)用重寫的getObject方法
            object = factory.getObject();
        }
    }
   	.......
    return object;
}

也就是說當(dāng)我們getBean("userMapper")的時(shí)候其實(shí)是調(diào)用FactoryBean的getObject方法,代碼回到mybatis-spring項(xiàng)目的MapperFactoryBean類中的getObject方法

@Override
public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
}

@Override
public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
}

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

到最后發(fā)現(xiàn)是通過jdk的動(dòng)態(tài)代理來生成的對(duì)象,那么回答開始的問題

我就寫了個(gè)接口為什么能用?

因?yàn)閙ybatis在spring加載bean之前修改了beanDefinition,通過MapperScannerConfigurer類實(shí)現(xiàn)的BeanDefinitionRegistryPostProcessor接口中將我們定義的一些mapper接口的BeanDefinition的BeanClass屬性修改為了MapperFactoryBean,而這個(gè)類實(shí)現(xiàn)了FactoryBean,我們獲取接口實(shí)際上是通過FactoryBean的getObject方法

到此這篇關(guān)于Mybatis-Spring源碼分析的文章就介紹到這了,更多相關(guān)Mybatis-Spring源碼分析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot配置文件啟動(dòng)加載順序的方法步驟

    SpringBoot配置文件啟動(dòng)加載順序的方法步驟

    SpringBoot的啟動(dòng)加載順序涉及多個(gè)步驟和組件,通過分層和優(yōu)先級(jí)機(jī)制加載配置文件,確保在啟動(dòng)時(shí)正確配置應(yīng)用程序,本文就來介紹一下SpringBoot配置文件啟動(dòng)加載順序的方法步驟,感興趣的可以了解一下
    2024-11-11
  • 持久層ORM框架Hibernate框架的使用及搭建方式

    持久層ORM框架Hibernate框架的使用及搭建方式

    Hibernate是一個(gè)開放源代碼的對(duì)象關(guān)系映射框架,它對(duì)JDBC進(jìn)行了非常輕量級(jí)的對(duì)象封裝,使得Java程序員可以隨心所欲的使用對(duì)象編程思維來操縱數(shù)據(jù)庫(kù),本文重點(diǎn)給大家介紹持久層ORM框架Hibernate框架的使用及搭建方式,感興趣的朋友一起看看吧
    2021-11-11
  • Java單例模式實(shí)現(xiàn)靜態(tài)內(nèi)部類方法示例

    Java單例模式實(shí)現(xiàn)靜態(tài)內(nèi)部類方法示例

    這篇文章主要介紹了Java單例模式實(shí)現(xiàn)靜態(tài)內(nèi)部類方法示例,涉及構(gòu)造函數(shù)私有化等相關(guān)內(nèi)容,需要的朋友可以了解下。
    2017-09-09
  • 在SpringBoot中如何利用Redis實(shí)現(xiàn)互斥鎖

    在SpringBoot中如何利用Redis實(shí)現(xiàn)互斥鎖

    當(dāng)我們利用Redis存儲(chǔ)熱點(diǎn)數(shù)據(jù)時(shí),突然就過期失效或者被刪除了,導(dǎo)致大量請(qǐng)求同時(shí)訪問數(shù)據(jù)庫(kù),增加了數(shù)據(jù)庫(kù)的負(fù)載,為減輕數(shù)據(jù)庫(kù)的負(fù)載我們利用互斥鎖,本文重點(diǎn)介紹在SpringBoot中如何利用Redis實(shí)現(xiàn)互斥鎖,感興趣的朋友一起看看吧
    2023-09-09
  • JAVA中的隊(duì)列(Queue)詳解

    JAVA中的隊(duì)列(Queue)詳解

    這篇文章主要介紹了JAVA中的隊(duì)列(Queue)詳解,隊(duì)列是一種特殊的線性表,遵循先入先出、后入后出的基本原則,一般來說,它只允許在表的前端進(jìn)行刪除操作,需要的朋友可以參考下
    2023-07-07
  • springboot如何使用@ConfigurationProperties封裝配置文件

    springboot如何使用@ConfigurationProperties封裝配置文件

    springboot如何使用@ConfigurationProperties封裝配置文件的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • SpringBoot自定義注解解決公共字段填充問題解決

    SpringBoot自定義注解解決公共字段填充問題解決

    修改時(shí)間,修改人等字段時(shí),這些字段屬于公共字段,本文主要介紹了SpringBoot自定義注解解決公共字段填充問題解決,使用它的好處就是可以統(tǒng)一對(duì)這些字段進(jìn)行處理,感興趣的可以了解一下
    2024-07-07
  • Mybatis逆向工程時(shí)失敗問題及解決

    Mybatis逆向工程時(shí)失敗問題及解決

    這篇文章主要介紹了Mybatis逆向工程時(shí)失敗問題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Spring深入探索AOP切面編程

    Spring深入探索AOP切面編程

    Spring是一個(gè)廣泛應(yīng)用的框架,SpringAOP則是Spring提供的一個(gè)標(biāo)準(zhǔn)易用的aop框架,依托Spring的IOC容器,提供了極強(qiáng)的AOP擴(kuò)展增強(qiáng)能力,對(duì)項(xiàng)目開發(fā)提供了極大地便利
    2022-07-07
  • Java經(jīng)典面試題匯總--多線程

    Java經(jīng)典面試題匯總--多線程

    本篇總結(jié)的是Java多線程相關(guān)的面試題,后續(xù)會(huì)持續(xù)更新,希望我的分享可以幫助到正在備戰(zhàn)面試的實(shí)習(xí)生或者已經(jīng)工作的同行,如果發(fā)現(xiàn)錯(cuò)誤還望大家多多包涵,不吝賜教,謝謝
    2021-06-06

最新評(píng)論

玛多县| 双峰县| 龙口市| 霍林郭勒市| 金湖县| 大名县| 商水县| 承德县| 嘉鱼县| 崇左市| 康马县| 呼玛县| 镇安县| 孟津县| 千阳县| 广德县| 太仆寺旗| 汤原县| 商水县| 辽中县| 新源县| 大名县| 资中县| 勐海县| 高平市| 云浮市| 磐安县| 安溪县| 濉溪县| 金阳县| 上栗县| 永川市| 新民市| 德格县| 玉田县| 万年县| 滕州市| 扬州市| 永定县| 望谟县| 色达县|