(1). 目的
研究mybatis-spring-boot-starter,看下有哪些配置项以及可扩展项.
(2). 添加mybatis starter依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
(3). 寻找mybatis-spring-boot-starter入口
发现:mybatis-spring-boot-starter是一个jar,但是没有相应的SPI(EnableAutoConfiguration).
那么,就看下POM依赖信息吧,期望在相应的POM依赖里有SPI.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 重点:看这个名字就知道是SPI -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</dependency>
</dependencies>
(4). mybatis-spring-boot-autoconfigure.jar/META-INF/spring.factories
# Auto Configure
# 我们关注:MybatisAutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
(5). MybatisAutoConfiguration
// 1. Spring认识这是一个配置类
@org.springframework.context.annotation.Configuration
// 2. CllassPath下有:SqlSessionFactory/SqlSessionFactoryBean这个配置类才能生效.
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
// 3. Spring容器中要有一个:DataSource
@ConditionalOnSingleCandidate(DataSource.class)
// 4. 启用配置类(MybatisProperties)
@EnableConfigurationProperties(MybatisProperties.class)
// 5. 在加载MybatisAutoConfiguration之前,要先加载:DataSourceAutoConfiguration/MybatisLanguageDriverAutoConfiguration
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
// 配置信息
private final MybatisProperties properties;
// 拦截器
private final Interceptor[] interceptors;
// 类型处理器
private final TypeHandler[] typeHandlers;
// MyBatis 3.2提供的(Pluggable Scripting Languages For Dynamic SQL)
private final LanguageDriver[] languageDrivers;
// 资源加载器
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
// **********************************************************
// 如果你想对:Configuration对象进行扩展配置,可以实现:ConfigurationCustomizer.customize(Configuration configuration)
// **********************************************************
private final List<ConfigurationCustomizer> configurationCustomizers;
// 6. 构造器
public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider,
ObjectProvider<TypeHandler[]> typeHandlersProvider, ObjectProvider<LanguageDriver[]> languageDriversProvider,
ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.typeHandlers = typeHandlersProvider.getIfAvailable();
this.languageDrivers = languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
} //end
// 7. 由于该类实现了:InitializingBean,所以Spring会回调该方法.
public void afterPropertiesSet() {
//委托给:checkConfigFileExists
checkConfigFileExists();
} // end checkConfigFileExists
private void checkConfigFileExists() {
// mybatis.check-config-location = false
// mybatis.config-location = null
// mybatis.config-location: 是我们平时配置mybatis的入口配置文件,而非*Mapper.xml
if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {
Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());
Assert.state(resource.exists(),
"Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");
}
} // end checkConfigFileExists
// 8. 这一步是构建:SqlSessionFactory
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
// mybatis.config-location
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
// 1. 如果配置项:mybatis.configuration为空
// 1.1 创建一个:Configuration对象
// 1.2 获取所有ConfigurationCustomizer的实现类,并挨个调用:ConfigurationCustomizer.customize(Configuration)
// 上一步主要是留个Hook给开发,允许开发人员对:Configuration对象进行配置.
applyConfiguration(factory);
// mybatis.configuration-properties
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
// 配置拦截器:interceptors
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
// 配置:databaseIdProvider
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
// 实体所在的package名称,多package之间用逗号分隔
// mybatis.type-aliases-package
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
// mybatis.type-aliases-super-type
if (this.properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
}
// 可以自定义类型处理器(比较:BigDecimal),多个之间用逗号分隔
// mybatis.type-handlers-package
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
//
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
factory.setTypeHandlers(this.typeHandlers);
}
// 指定*Mapper.xml文件
// mybatis.mapper-locations = []
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
// 下面这几个留着以后分析MyBatis新的特性再详解.
Set<String> factoryPropertyNames = Stream
.of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
// Need to mybatis-spring 2.0.2+
factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
defaultLanguageDriver = this.languageDrivers[0].getClass();
}
}
if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
// Need to mybatis-spring 2.0.2+
factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
}
return factory.getObject();
} // end sqlSessionFactory
// 9. 创建:SqlSessionTemplate
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
// mybatis.executorType
// 根据不同的:ExecutorType创建:Executor
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
} // end sqlSessionTemplate
}
(6). MybatisAutoConfiguration$AutoConfiguredMapperScannerRegistrar
向Spring容器中注册一个Bean(MapperScannerConfigurer),MapperScannerConfigurer的主要职责是把IMapper接口与IMapper.xml进行关联(动态代理)
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware,
ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
// 2. 因为实现了:ImportBeanDefinitionRegistrar
// Spring会回调访方法
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
return;
}
logger.debug("Searching for mappers annotated with @Mapper");
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
}
// *********************************************************
// 向Spring中注册一个Bean:MapperScannerConfigurer
// *********************************************************
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
// MapperScannerConfigurer.setProcessPropertyPlaceHolders
builder.addPropertyValue("processPropertyPlaceHolders", true);
// MapperScannerConfigurer.setAnnotationClass
builder.addPropertyValue("annotationClass", Mapper.class);
// MapperScannerConfigurer.setBasePackage
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
// 配置属性
Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
// Need to mybatis-spring 2.0.2+
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
}
if (propertyNames.contains("defaultScope")) {
// Need to mybatis-spring 2.0.6+
builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
}
// *************************************************
// 向Spring中注册Bean(MapperScannerConfigurer)
// *************************************************
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
} // end registerBeanDefinitions
// 1.因为实现了:BeanFactoryAware
// 所以,Spring会回调该方法,并设置:BeanFactory(ApplicationContext)
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
} // end setBeanFactory
} // end
(7). 总结
mybatis-spring-boot-starter的核心配置类是:MybatisAutoConfiguration,它的职责如下:
- 注入开发人员自定义的ConfigurationCustomizer对象(ConfigurationCustomizer可以对Configuration进行配置).
- 创建SqlSessionFactory.
- 创建SqlSessionTemplate.
- 创建MapperScannerConfigurer,它主要负责把Mapper.java进行动态代理(与Mapper.xml进行关联).
- MapperScannerConfigurer类属于MyBatis与Spring的整合,在这里我不剖析了.