天天看點

mybatis源碼解析--mapper代理對象的生成過程

我們平常在使用mybatis的時候隻需要生成mapper接口和與其對應的xml檔案就行了,我們就可以把這個接口當作一個bean,可以往其他的bean中注入了。我們沒有實作mapper接口,為什麼可以使用接口中的方法呢?原因是雖然我們沒有實作接口,但是通過配置檔案,spring為我們生成了接口的代理類。

讓我們從配置檔案入手,從源碼中一探究竟。

mybatis源碼解析--mapper代理對象的生成過程

MapperScannerConfigurer這個類是負責掃描mapper接口所在的包的,它把掃描到的接口解析成一個個的bean定義(BeanDefinition),來看一下源碼:

mybatis源碼解析--mapper代理對象的生成過程

MapperScannerConfigurer實作了兩個重要接口,如圖所示。

實作BeanDefinitionRegistryPostProcessor接口,我們可以自定義注冊bean過程,要實作的方法是

postProcessBeanDefinitionRegistry()

實作InitializingBean接口的afterPropertiesSet()方法,可以在bean建立之後初始化的時候做一些操作。

現在進入MapperScannerConfigurer的postProcessBeanDefinitionRegistry()方法

mybatis源碼解析--mapper代理對象的生成過程

該方法内部把掃描mapper接口的工作委托給了ClassPathMapperScanner類,該類繼承自ClassPathBeanDefinitionScanner,進入它的scan()方法:

mybatis源碼解析--mapper代理對象的生成過程

ClassPathBeanDefinitionScanner中的doScan()方法:

mybatis源碼解析--mapper代理對象的生成過程

doScan方法真正的負責生成bean定義。

ClassPathMapperScanner重載的doScan()方法:

mybatis源碼解析--mapper代理對象的生成過程

重載的doScan方法對生成的mapper的bean定義做了進一步處理,進入processBeanDefinitions()方法:

mybatis源碼解析--mapper代理對象的生成過程

紅色部分為該方法的核心,mapperInterface設定的值是mapper接口的帶包名的路徑名稱;

definition.setBeanClass()把原來的BeanClass的類型替換成了MapperFactoryBean類型,這個是Mapper接口加載定義階段最重要的一步。是生成代理類的關鍵。

檢視MapperFactoryBean的定義:

mybatis源碼解析--mapper代理對象的生成過程
mybatis源碼解析--mapper代理對象的生成過程
mybatis源碼解析--mapper代理對象的生成過程

MapperFactoryBean實作了FactoryBean接口,實作了FactoryBean接口的類型在調用getBean(beanName)既通過名稱擷取對象時,傳回的對象不是本身類型的對象,而是通過實作接口中的getObject()方法傳回的對象。

mybatis源碼解析--mapper代理對象的生成過程

MapperFactoryBean實作了FactoryBean接口InitializingBean接口,在對象初始化的時候會調用它的afterPropertiesSet方法,該方法中首先調用了checkDaoConfig()方法,MapperFactoryBean重載的checkDaoConfig()如下:

mybatis源碼解析--mapper代理對象的生成過程

Configuration configuration = getSqlSession().getConfiguration();這一句中的getSqlSession()擷取的SqlSession

對象就是文章開頭的配置檔案中配置的。

Configuration 是一個重要的配置類

mybatis源碼解析--mapper代理對象的生成過程

進入 configuration.addMapper(this.mapperInterface)方法中:

mybatis源碼解析--mapper代理對象的生成過程

在configuration内部對Mapper的操作都委托給了mapperRegistry對象,進入它的addMapper(type)方法,這裡的參數type就是一個mapper接口的類型(如:com.st.mapper.UserMapper.java):

mybatis源碼解析--mapper代理對象的生成過程

knownMappers.put(type, new MapperProxyFactory(type));這一步為我們建立了mapper 的代理工廠類對象,并把它放入了knownMappers這個Map中。MapperProxyFactory這個類我們下面再講。

MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);

parser.parse();

這兩句完成了mapper接口對應的xml檔案的解析,xml檔案中的每一個方法都被解析成了一個MappedStatement對象(代碼太長,不再展開),并添加到了configuration對象中:

mybatis源碼解析--mapper代理對象的生成過程

對上面的内容的總結:

mapper接口的定義在bean加載階段會被替換成MapperFactoryBean類型,在spring容器初始化的時候會給我們生成MapperFactoryBean類型的對象,在該對象生成的過程中調用afterPropertiesSet()方法,為我們生成了一個

MapperProxyFactory類型的對象存放于Configuration裡的MapperRegistry對象中,同時解析了mapper接口對應的xml檔案,把每一個方法解析成一個MappedStatement對象,存放于Configuration裡的mappedStatements

這個Map集合中。

下面看一下MapperFactoryBean的getObject()方法,看看mapper代理對象是如何生成的:

mybatis源碼解析--mapper代理對象的生成過程
mybatis源碼解析--mapper代理對象的生成過程

跟蹤代碼最後調用的是MapperRegistry.getMapper()方法給我們傳回了mapper代理對象。

現在來看一下MapperProxyFactory這個類:

mybatis源碼解析--mapper代理對象的生成過程

MapperProxy是被代理的對象,看下這個類:

mybatis源碼解析--mapper代理對象的生成過程

在invoke()方法中最終執行的是mapperMethod.execute(sqlSession, args);方法。

來看一下MapperMethod這個類:

mybatis源碼解析--mapper代理對象的生成過程

execute()這個方法最終負責執行我們mapper接口中方法,它會判斷要執行的方法的類型,然後調用sqlSession對應的方法類型來執行,并放回結果。

繼續閱讀