文章目录
- Mybatis和SpringBoot的集成
- 一、MyBatis和SpringBoot
- 二、MybatisAutoConfiguration
- 2.1 属性
- 2.2 配置SqlSessionFactory
- 2.3 配置SqlSessionTemplate
- 2.4 配置MapperFactoryBean
- 三、MybatisProperties
- 四、小结
Mybatis和SpringBoot的集成
- 前面介绍了MyBatis和Spring的集成,再来看和SpringBoot的整合应该会比较好理解了
一、MyBatis和SpringBoot
- 和SpringBoot集成MyBatis的话,主要需要增加下面这个依赖:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
- mybatis-spring-boot-starter本身没有提供Jar包和Class文件,只有引入的依赖,如下:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot</artifactId>
<version>1.3.2</version>
</parent>
<artifactId>mybatis-spring-boot-starter</artifactId>
<name>mybatis-spring-boot-starter</name>
<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>
<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>
</project>
- 这个包主要会引入mybatis、mybatis-spring以及mybatis-spring-boot-autoconfigure(其他的不是关键),因此MyBatis和SpringBoor集成的关键是mybatis-spring-boot-autoconfigure 这个包;
- 从下面的截图看出,mybatis-spring-boot-autoconfigure本身比较简单,它通过spring.factories注册一个配置类,本身也就两三个类文件,关键还是在 MybatisAutoConfiguration 这个配置类。只要SpringBoot项目开启了 @EnableAutoConfiguration ,那么该配置文件就会自动装配,而SpringBoot的启动注解 @SpringBootApplication 就默认启用了 @EnableAutoConfiguration;
二、MybatisAutoConfiguration
- MybatisAutoConfiguration 是MyBatis和SpringBoot集成的关键
2.1 属性
/**
* MyBatis的自动配置,提供 SqlSessionFactory 和 SqlSessionTemplate
*
* {@link EnableAutoConfiguration Auto-Configuration} for Mybatis. Contributes a
* {@link SqlSessionFactory} and a {@link SqlSessionTemplate}.
*
* 如果使用了MapperScan或者配置文件配置了接口的包,就会使用这些,如果没有,那么就会在自动配置的包下去扫描
* If {@link org.mybatis.spring.annotation.MapperScan} is used, or a
* configuration file is specified as a property, those will be considered,
* otherwise this auto-configuration will attempt to register mappers based on
* the interface definitions in or under the root auto-configuration package.
*
*/
//有DataSource这个Bean,并且classpath下有SqlSessionFactory和SqlSessionFactoryBean时这个配置类才会生效,
//并且会在DataSourceAutoConfiguration之后装配
@Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
//配置
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
private final List<ConfigurationCustomizer> configurationCustomizers;
//构造方法
public MybatisAutoConfiguration(MybatisProperties properties,
ObjectProvider<Interceptor[]> interceptorsProvider,
ResourceLoader resourceLoader,
ObjectProvider<DatabaseIdProvider> databaseIdProvider,
ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
}
//初始化之前,检查配置文件是否存在
@PostConstruct
public void checkConfigFileExists() {
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)");
}
}
}
2.2 配置SqlSessionFactory
- 如果没有SqlSessionFactory对应的Bean,则装配一个,也是通过SqlSessionFactoryBean,和spring集成mybatis几乎一样
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new Configuration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
customizer.customize(configuration);
}
}
factory.setConfiguration(configuration);
if (this.properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(this.properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(this.interceptors)) {
factory.setPlugins(this.interceptors);
}
if (this.databaseIdProvider != null) {
factory.setDatabaseIdProvider(this.databaseIdProvider);
}
if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
factory.setMapperLocations(this.properties.resolveMapperLocations());
}
return factory.getObject();
}
2.3 配置SqlSessionTemplate
- 如果没有 SqlSessionTemplate 对应的Bean,则装配一个
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
2.4 配置MapperFactoryBean
- 看下自动配置MapperFactoryBean,如果用户使用了@MapperScan 注解,那么就会注册该Bean,如果用户没有使用,那么就不会有这个Bean,那么此时就会导入AutoConfiguredMapperScannerRegistrar,在AutoConfiguredMapperScannerRegistrar里面完成 @Mapper 接口的扫描
/**
* {@link org.mybatis.spring.annotation.MapperScan} ultimately ends up
* creating instances of {@link MapperFactoryBean}. If
* {@link org.mybatis.spring.annotation.MapperScan} is used then this
* auto-configuration is not needed. If it is _not_ used, however, then this
* will bring in a bean registrar and automatically register components based
* on the same component-scanning path as Spring Boot itself.
*/
@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 : 扫描@MapperScan 注解的类。
/**
* 扫描MapperScan
* This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use
* {@link org.mybatis.spring.annotation.MapperScan} but this will get typed
* mappers working correctly, out-of-the-box, similar to using Spring Data JPA repositories.
*/
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
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);
}
}
}
三、MybatisProperties
- MybatisProperties是MyBatis和SpringBoot整合之后的配置文件,如下:
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {
//配置前缀
public static final String MYBATIS_PREFIX = "mybatis";
//MyBatis配置文件路径
private String configLocation;
//xml映射文件路径
private String[] mapperLocations;
//别名映射文件
private String typeAliasesPackage;
//类型处理器包路径
private String typeHandlersPackage;
//executorType 包括 batch / reuse / simple
private ExecutorType executorType;
//直接设置一个配置对象
private Properties configurationProperties;
}
- 和SpringBoot集成后MyBatis的配置:
四、小结
- Mybatis和SpringBoot集成原理本身没有太多新东西,主要是利用了spring.factories机制 和 @ConditionalOnMissingBean 来实现,前者来导入一个配置类,后者当容器不存在某个Bean的时候则自动装配一个Bean ,至于@Import和ImportBeanDefinitionRegistrar等都是Spring本身的功能机制了。
- 代码:SpringBoot集成MyBatis