文章目錄
從本篇文章開始,簡單分析下mybatis源碼。
本篇文章先分析下mapper接口是如何被掃描到spring容器中的。
springboot集成mybatis非常簡單,只需要在pom文件中添加如下依賴(本文分析的是1.3.2版本):
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
1. mybatis如何被加載啓動
在使用mybatis時,我們寫了很多的mapper接口,接口上面使用了@Mapper註解修飾;但是此註解是mybais中的內容,並非spring,也就是說spring並不認識此註解,那麼這些mapper接口是如何被加載到spring容器中的呢?
這部分內容,實際上是springboot的內容,至於springboot的啓動流程以及原理,如果不清楚可以先看下之前的分析文章《springboot源碼(一)啓動流程+自動配置原理分析》,
從這篇文章中,我們知道springboot默認會加載starter包中的spring.factories文件,並讀取其中的配置類。
上述mybatis-spring-boot-starter中並沒有這個spring.factories文件,但是當我們打開這個pom文件,發現它有如下依賴:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
從依賴的libraries中扎到此依賴,打開,發現存在spring.factories文件:
繼續打開此文件:
發現只有一個配置類MybatisAutoConfiguration,沒錯,入口就在這裏了。
2. MybatisAutoConfiguration
這是一個被@Configuration註解修飾bean,是配置類。
看一下類結構:
內容不多,包含兩個內部類,四個方法以及一些屬性。
先簡單介紹他們的作用:
- MybatisAutoConfiguration():構造方法;
- checkConfigFileExists():檢查是否需要加載存在的mybatis的config xml配置文件,有就加載;
- sqlSessionFactory(): 初始化SqlSessionFactory對象,這個是Mybatis的核心類之一。
- sqlSessionTemplate(): 初始化SqlSessionTemplate對象;
- MapperScannerRegistrarNotFoundConfiguration:這是一個內部類,也是配置類,核心是上面的@import註解,內容是下面的內部類AutoConfiguredMapperScannerRegistrar;
@org.springframework.context.annotation.Configuration
@Import({ AutoConfiguredMapperScannerRegistrar.class })
@ConditionalOnMissingBean(MapperFactoryBean.class)
public static class MapperScannerRegistrarNotFoundConfiguration {
@PostConstruct
public void afterPropertiesSet() {
logger.debug("No {} found.", MapperFactoryBean.class.getName());
}
}
- AutoConfiguredMapperScannerRegistrar:也是個內部類,提供了掃描Mapper接口的方法;
我們看下這個AutoConfiguredMapperScannerRegistrar,其中有一個方法:
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//掃描被@Mapper修飾的mapper接口
logger.debug("Searching for mappers annotated with @Mapper");
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
//得到要掃描的包
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
for (String pkg : packages) {
logger.debug("Using auto-configuration base package '{}'", pkg);
}
}
//設置只掃描@Mapper註解修飾的bean
scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
//執行掃描動作
scanner.doScan(StringUtils.toStringArray(packages));
} catch (IllegalStateException ex) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
}
}
此方法名字應該很熟悉了,他是ImportBeanDefinitionRegistrar接口方法的實現(不熟悉這個類的可以看下使用場景《spring @Import註解的作用和幾種使用方式》);
registerBeanDefinitions這個方法,看名字就知道它的作用是註冊bean定義,就是往spring容器中註冊,註冊的bean,就是mapper接口。作者通過第一行log打印出了此方法的功能:Searching for mappers annotated with @Mapper。
到這裏,我們基本已經知道了mybatis在springboot中到底是如何被掃描並註冊的。
下面我們詳細看下。
3. AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions
上面已經貼出了此方法的代碼,發現主要是通過ClassPathMapperScanner類來完成具體掃描動作。
其中核心方法是 scanner.doScan(StringUtils.toStringArray(packages));
@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 {
//處理bean定義
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
首先調用父類方法掃描所有mapper,並得到bean定義的集合;
然後,通過processBeanDefinitions(beanDefinitions);又對bean的定義進行了二次加工:
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
//遍歷每個mapper接口的bean定義進行加工處理
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
//構造函數中摻入mapper的名字
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
//每個mapper接口,都變爲了MapperFactoryBean;但是會通過上一行的構造函數參數(mapper名字)來區分每個mapper
definition.setBeanClass(this.mapperFactoryBean.getClass());
//添加了一個屬性addToConfig,值爲true
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
//下面兩個if進不去
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
//下面兩個if進不去
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
//注意設置的自動注入類型
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
此方法修改了mapper的bean定義,比較重要的有:
- 重置了beanClass爲代理類 MapperFactoryBean;此時mapper接口已經物是人非成爲別人了。
- 因爲每個mapper都變成了MapperFactoryBean,爲了區分他們,MapperFactoryBean的構造函數參數,傳入了mapper接口的名字;
- sqlSessionFactory屬性和sqlSessionTemplate屬性注意下,雖然這裏還沒把具體屬性設置進去,但是實例化的mapper的時候會用到這倆;
- *definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);設置注入類型爲按類型注入;sqlSessionFactory屬性和sqlSessionTemplate屬性就是因此才set注入的。
上述過程只是bean定義過程,這倆屬性會在mapper bean實例化的之後,進行填充;
上面這幾點比較重要,後續文章動態代理mapper接口都跟他們有關係。
本文先簡單分析到這裏,主要分析了mapper的被加載流程,到這裏mapper已經被註冊了,
至於mapper接口的實例化以及如何被動態代理以及sql如何執行等詳細內容,放在後續文章中分析: