開心一刻 html
中韓兩學生辯論。
中:端午節是屬於誰的?
韓:韓國人!
中:漢字是誰發明的?
韓:韓國人!
中:中醫是屬於誰的?
韓:韓國人!
中:那中國人到底發明過什麼?
韓:韓國人!spring
Mybatis源碼解析 - mapper代理對象的生成,你有想過嗎,咱們講到了mybatis操做數據庫的流程:先建立SqlSessionFactory,而後建立SqlSession,而後再建立獲取mapper代理對象,最後利用mapper代理對象完成數據庫的操做;Mapper代理對象的建立,利用的是JDK的動態代理,InvocationHandler是MapperProxy,後續Mapper代理對象方法的執行都會先通過MapperProxy的invoke方法。sql
可是,此時SqlSessionFactory的建立、SqlSession的建立以及mapper代理對象的獲取都是咱們手動操做的,實際應用中,mybatis每每也不會單獨使用,絕大多數都是集成在spring中,也就是說咱們無需手動去管理mybatis相關對象的生命週期,所有都由spring容器統一管理,那麼spring是何時在哪建立的mybatis的相關對象的呢?尤爲是mapper代理對象MapperProxy的建立數據庫
當springboot(其實仍是spring)集成mybatis後,mybatis的對象是交給spring容器管理的,只會實例化一次,而後伴隨着spring容器一直存在,直到spring容器銷燬springboot
Mybatis的自動配置類:MybatisAutoConfiguration,至於如何加載此類,可參考:spring-boot-2.0.3啓動源碼篇一 - SpringApplication構造方法
mybatis
MybatisAutoConfiguration會被當作配置類被spring解析,咱們來看看spring容器會今後配置類中解析到什麼app
建立了SqlSessionFactory實例(實際類型:DefaultSqlSessionFactory),並註冊到了spring容器;此時咱們應該還注意到less
@Import({ AutoConfiguredMapperScannerRegistrar.class })
AutoConfiguredMapperScannerRegistrar繼承了ImportBeanDefinitionRegistrar(注意看類註釋,有興許的能夠更深刻的研究下),那麼它的registerBeanDefinitions也會被調用ide
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { logger.debug("Searching for mappers annotated with @Mapper"); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); try { if (this.resourceLoader != null) { scanner.setResourceLoader(this.resourceLoader); } // 獲取啓動類所在的包,如:com.lee.shiro,會做爲掃描開始的base package,通常只會有一個,但支持多個 List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (logger.isDebugEnabled()) { for (String pkg : packages) { logger.debug("Using auto-configuration base package '{}'", pkg); } } scanner.setAnnotationClass(Mapper.class); // 設置掃誰,Mapper註解是被掃描對象 scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(packages)); // 掃描全部mapper,進行bean定義處理 } catch (IllegalStateException ex) { logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex); } }
以咱們啓動類所在的包(com.lee.shiro)爲基包,掃描全部的mapper,而後修改全部mapper在spring容器中的bean定義,將mapper的beanClass所有指向了MapperFactoryBeanspring-boot
MapperFactoryBean繼承SqlSessionDaoSupport,SqlSessionDaoSupport有兩個方法用來設置SqlSession
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!this.externalSqlSession) { this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSession = sqlSessionTemplate; this.externalSqlSession = true; }
能夠看到SqlSession的實際類型是:SqlSessionTemplate,SqlSessionTemplate在MybatisAutoConfiguration以@Bean方式建立的
Spring在建立Service實例:UserServiceImpl的時候,發現依賴mapper(可能還有其餘的實例依賴mapper),那麼就會去spring容器獲取mapper實例,沒有則進行建立,而後注入進來(依賴注入);具體建立過程以下
if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { // 建立mapper對象,beanName:com.lee.shiro.mapper.UserMapper,建立出來的實例其實是MapperFactoryBean類型 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實例的對象,若是是FactoryBean,則獲取bean實例自己或其建立的對象 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }
由於Spring在mapper掃描的時候,將全部mapper bean定義中的beanClass設置成了MapperFactoryBean(繼承了FactoryBean),因此經過createBean方法建立的mapper實例其實是MapperFactoryBean對象,而後經過
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { // Don't let calling code try to dereference the factory if the bean isn't a factory. // isFactoryDereference方法判斷name中是否有&字符 if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass()); } } // Now we have the bean instance, which may be a normal bean or a FactoryBean. // If it's a FactoryBean, we use it to create a bean instance, unless the // caller actually wants a reference to the factory. // 此時的beanInstance多是一個普通bean,也多是一個FactoryBean // 若是是一個FactoryBean,那麼就用它建立想要的bean實例 // 此if表示,若是beanInstance是普通bean,或者原本就想要FactoryBean實例,則直接返回beanInstance if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } Object object = null; if (mbd == null) { object = getCachedObjectForFactoryBean(beanName); } // 此時代表beanInstance是一個FactoryBean,而且不是想要FactoryBean實例 if (object == null) { // Return bean instance from factory. FactoryBean<?> factory = (FactoryBean<?>) beanInstance; // Caches object obtained from FactoryBean if it is a singleton. if (mbd == null && containsBeanDefinition(beanName)) { mbd = getMergedLocalBeanDefinition(beanName); } boolean synthetic = (mbd != null && mbd.isSynthetic()); // 經過FactoryBean實例建立咱們想要的實例 object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
獲取真正想要的bean實例,若是beanInstance是普通bean,或者原本就想要FactoryBean實例(beanName中有&),那麼直接返回beanInstance,不然用FactoryBean實例來建立咱們想要的實例對象。說回來就是會調用MapperFactoryBean的getObject()方法來獲取Mapper的代理對象
後續流程就能夠參考:Mybatis源碼解析 - mapper代理對象的生成,你有想過嗎
至此,前情回顧中的問題也就清晰了
一、自動配置的過程當中,spring會掃描全部的mapper,並將全部mapper bean定義中的beanClass指向MapperFactoryBean;
二、建立mapper實例的時候,根據bean定義建立的實例其實是MapperFactoryBean實例,而後再利用MapperFactoryBean獲取mapper實例(調用MapperFactoryBean的getObject方法,mybatis會利用jdk的動態代理建立mapper代理對象);
三、對比Mybatis源碼解析 - mapper代理對象的生成,你有想過嗎,其實就是將咱們手動建立的過程經過自動配置,將建立過程交給了spring;
四、關於spring的FactoryBean,有時間再從新開一篇給你們講講