文章目錄
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本身的功能機制了。