在項(xiàng)目中大多數(shù)用的都是@MapperScan注解,指定basePackages,掃描mybatis Mapper接口類,另外一種方式是用@Mapper注解,其實(shí)這兩種方法掃描配置用的是一個(gè)地方,只是掃描入口不同。
@MapperScan是根據(jù)其注解上MapperScannerRegistrar進(jìn)行自動(dòng)配置的,最終調(diào)用的自動(dòng)配置代碼和下面的代碼一致
@Mapper自動(dòng)配置的程序入口是?MybatisAutoConfiguration類的最下面,位置在mybatis-spring-boot-starter包下面的mybatis-spring-boot-autoconfigure,根據(jù)名字可以看出,這個(gè)是自動(dòng)配置,這個(gè)地方用到了spring-boot的自動(dòng)配置相關(guān)注解
上面代碼的邏輯是 如果標(biāo)注了@MapperScan 的注解,將會(huì)生成 MapperFactoryBean, 如果沒有標(biāo)注@MapperScan 也就是沒有MapperFactoryBean的實(shí)例,就走@Import里面的配置,下面看看AutoConfiguredMapperScannerRegistrar的配置,它是MybatisAutoConfiguration類下的內(nèi)部類
上面的代碼的邏輯就是初始化ClassPathMapperScanner掃描器,這個(gè)掃描器繼承了spirng的ClassPathBeanDefinitionScanner的主要作用就是掃描Mapper接口類進(jìn)行配置并注冊(cè)為spring bean,首先看看核心的doScan()方法,@MapperScan注解的自動(dòng)配置也是用了這個(gè)注解,這個(gè)方法做了什么,也是貼圖如下:
這個(gè)類主要就是調(diào)用父類ClassPathBeanDefinitionScanner中的doScan()方法,獲取所有的類定義,之后添加自己的邏輯:processBeanDefinitions()
下面看看processBeanDefinitions()方法主要做了什么,也是貼出核心代碼
注意上面涂黃的代碼,這個(gè)方法主要做的事情就是重新設(shè)置上面掃描出的BeanDefinition,設(shè)置構(gòu)造器的參數(shù),構(gòu)造器的參數(shù)為標(biāo)注了@Mapper的類的class,
做的事情其實(shí)是重寫B(tài)eanDefinition的BeanClass字段為MapperFactoryBean.class,并且將beanClass其實(shí)也就是MapperFactoryBean的構(gòu)造器參數(shù)設(shè)置為實(shí)際的標(biāo)注了@Mapper的接口,之后當(dāng)Mapper接口注入的時(shí)候,實(shí)際調(diào)用的是MapperFactoryBean中的getObject()獲取特定的mapper實(shí)例
如下是MapperFactoryBean的核心邏輯,mapperInterface字段是通過(guò)上面的?
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());這段代碼注入的
可見這個(gè)類實(shí)現(xiàn)了FactoryBean的接口(自由拓展注入的類),以及繼承了SqlSessionDaoSupport,繼承SqlSessionDaoSupport的主要目的是為了獲取SqlSession,通過(guò)SqlSession獲取具體的mapper代理類(這個(gè)也是核心方法),
綜上所述,首先根據(jù)標(biāo)注的@MapperScan 獲取basePackage或者根據(jù)@Mapper獲取所在packages,之后通過(guò)?ClassPathMapperScanner去掃描包,獲取所有Mapper接口類的BeanDefinition,之后具體配置,設(shè)置beanClass為MapperFactoryBean,設(shè)置MapperFactoryBean的構(gòu)造器參數(shù)為實(shí)際的Mapper接口類,通過(guò)ClassPathBeanDefinitionScanner父類進(jìn)行bean注冊(cè),自動(dòng)注入的時(shí)候,就會(huì)調(diào)用MapperFactoryBean的getObject方法獲取實(shí)際類型的實(shí)例。