欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mybatis-Spring源碼分析圖解

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

Mybatis-Spring

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

一個(gè)問(wèn)題:

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

首先來(lái)看,在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ī)上篇博客寫(xiě)過(guò)了,這里就不再贅述

那么它必然實(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容器中在最后一行,先來(lái)看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è)類重寫(xiě)了doScan方法,所以會(huì)調(diào)用子類重寫(xiě)的方法

@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;
}

通過(guò)包名獲取BeanDefinitionHolder,現(xiàn)在它獲取到了User接口的BeanDefinitionHolder,然后判斷如果BeanDefinitionHolder的集合為空,也就是沒(méi)有找到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)造而不是無(wú)參來(lái)初始化對(duì)象我也不知道.....這和spring推斷構(gòu)造方法有關(guān),以后學(xué)會(huì)了在來(lái)寫(xiě))

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

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

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

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中沒(méi)有

然后執(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不為空,并且以&開(kāi)頭返回true

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

來(lái)看例子

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

回來(lái)看代碼,如果這個(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,想通過(guò)工廠獲取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)前我們是想通過(guò)FactoryBean獲取對(duì)象,那么不進(jìn)if,繼續(xù)下面的代碼

Object object = null;
// 如果beanDefinition為null,則嘗試從緩存中獲取給定的FactoryBean公開(kāi)的對(duì)象
if (mbd == null) {
    //嘗試從緩存中加載bean
    object = getCachedObjectForFactoryBean(beanName);
}
// 未能從緩存中獲得FactoryBean公開(kāi)的對(duì)象,則說(shuō)明該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;

主要來(lái)看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)用重寫(xiě)的getObject方法
            object = factory.getObject();
        }
    }
   	.......
    return object;
}

也就是說(shuō)當(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)是通過(guò)jdk的動(dòng)態(tài)代理來(lái)生成的對(duì)象,那么回答開(kāi)始的問(wèn)題

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

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

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

相關(guān)文章

最新評(píng)論