Mybatis-Spring源碼分析圖解
Mybatis-Spring
當我們使用mybatis和spring整合后為什么下面的代碼可以運行?
一個問題:
我就寫了個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的作用是將一個類添加到spring容器中,點進這個類看看
它實現(xiàn)了一個BeanDefinitionRegistryPostProcessor
接口,關(guān)于這個接口的作用和執(zhí)行時機上篇博客寫過了,這里就不再贅述
那么它必然實現(xiàn)postProcessBeanDefinitionRegistry
方法,點擊這個方法查看
@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)); }
其中將接口注冊到spring容器中在最后一行,先來看ClassPathMapperScanner
這個類,它繼承了ClassPathBeanDefinitionScanner
這個掃描器
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); }
這個是spring內(nèi)部的掃描方法,當它走到doScan的時候,因為ClassPathMapperScanner這個類重寫了doScan方法,所以會調(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)在有一個UserMapper的,進入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)建這個userMapper這個Bean的時候會使用這個有參構(gòu)造將當前這個UserMapper類型設(shè)置到mapperInterface屬性上(為啥使用有參構(gòu)造而不是無參來初始化對象我也不知道.....這和spring推斷構(gòu)造方法有關(guān),以后學會了在來寫)
這個MapperFactoryBean實現(xiàn)了一個FactoryBean
接口,這個接口可以讓我們自定義獲取bean的操作
回到spring的代碼,例如當我們使用context.getBean(xxx.class)的時候
spring將xxx.class類型解析為bean名稱,通過名稱去獲取
protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { //獲取對應(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)建對象的地方 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(其實就是三個Map,key為bean名稱,value是對象),那現(xiàn)在是首次獲取map中沒有
然后執(zhí)行到下面的createBean,當創(chuàng)建完這個bean后spring需要判斷這個bean是一個普通bean還是一個FactoryBean,程序員是想要獲取普通bean還是FactoryBean,還是FactoryBean的getObject方法返回的從工廠生成的對象
咱們一段一段看
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)
的作用是一個字符串判斷,當返回傳入名稱是否為工廠,如果name不為空,并且以&開頭返回true
這個方法在下面的判斷也使用到了,記一下它的作用即可
來看例子
在我們使用FactoryBean通過context.getBean("工廠Bean名稱")的時候獲取的是FactoryBean的getObject生成的對象,如果我們想獲取FactoryBean的引用則需要在名稱前面加一個&
符號
回來看代碼,如果這個bean的引用是一個NullBean類型則直接返回引用,下面有做了一個判斷
if (!(beanInstance instanceof FactoryBean))
再次判斷這個bean是不是一個FactoryBean,如果為true則拋出異常,這個好理解,因為我們在getBean的時候完全可以將一個普通的bean名稱前面加上&符號
主要的判斷在下面的這個if
if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; }
現(xiàn)在有3中情況
1.當前的bean是一個普通的bean
第一個條件false 取反 true 第二個條件false 結(jié)果true,直接返回bean實例
2.當前是一個FactoryBean,想通過工廠獲取Bean
第一個條件 true 取反false 第二個條件false 結(jié)果false,進行下面的操作
3.當前是一個FactoryBean,想獲取工廠的引用
第一個條件 true 取反 false 第二個條件 true 結(jié)果 true 直接返回factoryBean實例
當前我們是想通過FactoryBean獲取對象,那么不進if,繼續(xù)下面的代碼
Object object = null; // 如果beanDefinition為null,則嘗試從緩存中獲取給定的FactoryBean公開的對象 if (mbd == null) { //嘗試從緩存中加載bean object = getCachedObjectForFactoryBean(beanName); } // 未能從緩存中獲得FactoryBean公開的對象,則說明該bean是一個新創(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對象 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; }
也就是說當我們getBean("userMapper")的時候其實是調(diào)用FactoryBean的getObject方法,代碼回到mybatis-spring項目的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的動態(tài)代理來生成的對象,那么回答開始的問題
我就寫了個接口為什么能用?
因為mybatis在spring加載bean之前修改了beanDefinition,通過MapperScannerConfigurer類實現(xiàn)的BeanDefinitionRegistryPostProcessor接口中將我們定義的一些mapper接口的BeanDefinition的BeanClass屬性修改為了MapperFactoryBean,而這個類實現(xiàn)了FactoryBean,我們獲取接口實際上是通過FactoryBean的getObject方法
到此這篇關(guān)于Mybatis-Spring源碼分析的文章就介紹到這了,更多相關(guān)Mybatis-Spring源碼分析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于SpringBoot服務(wù)端表單數(shù)據(jù)校驗的實現(xiàn)方式
這篇文章主要介紹了基于SpringBoot服務(wù)端表單數(shù)據(jù)校驗的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10Gateway網(wǎng)關(guān)自定義攔截器的不可重復讀取數(shù)據(jù)問題
這篇文章主要介紹了Gateway網(wǎng)關(guān)自定義攔截器的不可重復讀取數(shù)據(jù)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08Spring Security內(nèi)存中認證的實現(xiàn)
本文主要介紹了Spring Security內(nèi)存中認證的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-11-11Java在Excel中添加水印的實現(xiàn)(單一水印、平鋪水印)
這篇文章主要介紹了Java在Excel中添加水印的實現(xiàn)(單一水印、平鋪水印),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04springboot整合mybatis-plus 實現(xiàn)分頁查詢功能
這篇文章主要介紹了springboot整合mybatis-plus 實現(xiàn)分頁查詢功能,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09