<> stay springboot Used in the project mybatis Just introduce mybatis-spring-boot-starter Ready to use , The principle of automatic assembly is analyzed from the perspective of source code
<>1. Introduce dependency
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>
mybatis-spring-boot-starter</artifactId> <version>2.1.2</version> </dependency>
<>2. Import mybatis And mybatis-spring rely on
1. stay maven Found in Library mybatis-spring-boot-autoconfigure=》MATA-INF-spring.factories file , file
# springboot Will scan spring.factories In the file EnableAutoConfiguration Specified class # Injected into IOC In the container
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
a) The first class MybatisLanguageDriverAutoConfiguration, When there is LanguageDriver Take effect when
b)MybatisAutoConfiguration, This category is springboot integration mybatis Core class
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by
Fernflower decompiler) // package org.mybatis.spring.boot.autoconfigure; import
java.beans.FeatureDescriptor; import java.util.Iterator; import java.util.List;
import java.util.Set; import java.util.stream.Collectors; import java.util.
stream.Stream; import javax.sql.DataSource; import org.apache.ibatis.annotations
.Mapper; import org.apache.ibatis.mapping.DatabaseIdProvider; import org.apache.
ibatis.plugin.Interceptor; import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.
SqlSessionFactory; import org.apache.ibatis.type.TypeHandler; import org.mybatis
.spring.SqlSessionFactoryBean; import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperFactoryBean; import org.mybatis.spring.
mapper.MapperScannerConfigurer; import org.slf4j.Logger; import org.slf4j.
LoggerFactory; import org.springframework.beans.BeanWrapper; import org.
springframework.beans.BeanWrapperImpl; import org.springframework.beans.factory.
BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import
org.springframework.beans.factory.InitializingBean; import org.springframework.
beans.factory.ObjectProvider; import org.springframework.beans.factory.support.
BeanDefinitionBuilder; import org.springframework.beans.factory.support.
BeanDefinitionRegistry; import org.springframework.boot.autoconfigure.
AutoConfigurationPackages; import org.springframework.boot.autoconfigure.
AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.
ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.
ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.
condition.ConditionalOnSingleCandidate; import org.springframework.boot.
autoconfigure.jdbc.DataSourceAutoConfiguration; import org.springframework.boot.
context.properties.EnableConfigurationProperties; import org.springframework.
context.annotation.Bean; import org.springframework.context.annotation.
Configuration; import org.springframework.context.annotation.Import; import org.
springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.
springframework.core.io.Resource; import org.springframework.core.io.
ResourceLoader; import org.springframework.core.type.AnnotationMetadata; import
org.springframework.util.Assert; import org.springframework.util.CollectionUtils
; import org.springframework.util.ObjectUtils; import org.springframework.util.
StringUtils; // Specify the configuration class @Configuration
// When there is SqlSessionFactory.class,SqlSessionFactoryBean.class Take effect when @ConditionalOnClass(
{SqlSessionFactory.class, SqlSessionFactoryBean.class})
// When there is only one singleton DataSource Or a preference is specified DataSource Take effect when @ConditionalOnSingleCandidate(
DataSource.class) // Enable Mybatis Property configuration @EnableConfigurationProperties({
MybatisProperties.class})
// stay DataSourceAutoConfiguration,MybatisLanguageDriverAutoConfiguration After the configuration, it will be small
@AutoConfigureAfter({DataSourceAutoConfiguration.class,
MybatisLanguageDriverAutoConfiguration.class}) public class
MybatisAutoConfiguration implements InitializingBean { private static final
Logger logger= LoggerFactory.getLogger(MybatisAutoConfiguration.class); private
final MybatisProperties properties; private final Interceptor[] interceptors;
private final TypeHandler[] typeHandlers; private final LanguageDriver[]
languageDrivers; private final ResourceLoader resourceLoader; private final
DatabaseIdProvider databaseIdProvider; private final List<
ConfigurationCustomizer> configurationCustomizers; 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= (Interceptor[])interceptorsProvider.getIfAvailable(); this.
typeHandlers= (TypeHandler[])typeHandlersProvider.getIfAvailable(); this.
languageDrivers= (LanguageDriver[])languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader; this.databaseIdProvider = (
DatabaseIdProvider)databaseIdProvider.getIfAvailable(); this.
configurationCustomizers= (List)configurationCustomizersProvider.getIfAvailable(
); } public void afterPropertiesSet() { this.checkConfigFileExists(); } private
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)"); } }
// When IOC There's nothing in the container sqlSessionFactory Create when bean example @Bean @ConditionalOnMissingBean public
SqlSessionFactorysqlSessionFactory(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())); } this.
applyConfiguration(factory); 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 (this.
properties.getTypeAliasesSuperType() != null) { factory.setTypeAliasesSuperType(
this.properties.getTypeAliasesSuperType()); } if (StringUtils.hasLength(this.
properties.getTypeHandlersPackage())) { factory.setTypeHandlersPackage(this.
properties.getTypeHandlersPackage()); } if (!ObjectUtils.isEmpty(this.
typeHandlers)) { factory.setTypeHandlers(this.typeHandlers); } if (!ObjectUtils.
isEmpty(this.properties.resolveMapperLocations())) { factory.setMapperLocations(
this.properties.resolveMapperLocations()); } Set<String> factoryPropertyNames =
(Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).
getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.
toSet()); Class<? extends LanguageDriver> defaultLanguageDriver = this.
properties.getDefaultScriptingLanguageDriver(); if (factoryPropertyNames.
contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.
languageDrivers)) { factory.setScriptingLanguageDrivers(this.languageDrivers);
if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
defaultLanguageDriver= this.languageDrivers[0].getClass(); } } if (
factoryPropertyNames.contains("defaultScriptingLanguageDriver")) { factory.
setDefaultScriptingLanguageDriver(defaultLanguageDriver); } return factory.
getObject(); } private void applyConfiguration(SqlSessionFactoryBean factory) {
org.apache.ibatis.session.Configuration configuration = this.properties.
getConfiguration(); if (configuration == null && !StringUtils.hasText(this.
properties.getConfigLocation())) { configuration = new org.apache.ibatis.session
.Configuration(); } if (configuration != null && !CollectionUtils.isEmpty(this.
configurationCustomizers)) { Iterator var3 = this.configurationCustomizers.
iterator(); while(var3.hasNext()) { ConfigurationCustomizer customizer = (
ConfigurationCustomizer)var3.next(); customizer.customize(configuration); } }
factory.setConfiguration(configuration); }
// When IOC There's nothing in the container sqlSessionTemplate Create instantiation bean @Bean @ConditionalOnMissingBean public
SqlSessionTemplatesqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType= this.properties.getExecutorType(); return
executorType!= null ? new SqlSessionTemplate(sqlSessionFactory, executorType) :
new SqlSessionTemplate(sqlSessionFactory); }
// Profile import MybatisAutoConfiguration And AutoConfiguredMapperScannerRegistrar
// When there is no MapperFactoryBean.class, MapperScannerConfigurer.class @Configuration
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.
class}) public static class MapperScannerRegistrarNotFoundConfiguration
implements InitializingBean { public MapperScannerRegistrarNotFoundConfiguration
() { } public void afterPropertiesSet() { MybatisAutoConfiguration.logger.debug(
"Not found configuration for registering mapper bean using @MapperScan,
MapperFactoryBean and MapperScannerConfigurer."); } } public static class
AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware,
ImportBeanDefinitionRegistrar{ private BeanFactory beanFactory; public
AutoConfiguredMapperScannerRegistrar() { } public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if
(!AutoConfigurationPackages.has(this.beanFactory)) { MybatisAutoConfiguration.
logger.debug("Could not determine auto-configuration package, automatic mapper
scanning disabled."); } else { MybatisAutoConfiguration.logger.debug("Searching
for mappers annotated with @Mapper"); List<String> packages =
AutoConfigurationPackages.get(this.beanFactory); if (MybatisAutoConfiguration.
logger.isDebugEnabled()) { packages.forEach((pkg) -> { MybatisAutoConfiguration.
logger.debug("Using auto-configuration base package '{}'", pkg); }); }
BeanDefinitionBuilder builder= BeanDefinitionBuilder.genericBeanDefinition(
MapperScannerConfigurer.class); builder.addPropertyValue(
"processPropertyPlaceHolders", true); builder.addPropertyValue("annotationClass"
, Mapper.class); builder.addPropertyValue("basePackage", StringUtils.
collectionToCommaDelimitedString(packages)); BeanWrapper beanWrapper = new
BeanWrapperImpl(MapperScannerConfigurer.class); Stream.of(beanWrapper.
getPropertyDescriptors()).filter((x) -> { return x.getName().equals(
"lazyInitialization"); }).findAny().ifPresent((x) -> { builder.addPropertyValue(
"lazyInitialization", "${mybatis.lazy-initialization:false}"); }); registry.
registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.
getBeanDefinition()); } } public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory; } } }
You can see from the source code , When there is SqlSessionFactory.class,SqlSessionFactoryBean.class The two classes exist , And specified DataSource after , The configuration class takes effect , In force IOC If it does not exist in the container , Will be created sqlSessionFactory And sqlSessionTemplate instantiation bean, If applied elsewhere , It can be injected directly , In the injection sqlSessionFactory It needs to be injected DataSource,DataSource Where does it come from ?springboot-autoconfigure-jdbc There is a configuration class in , The configuration class is automatically annotated DataSource
stay MybatisAutoConfiguration
There is a configuration file like this , When these are missing from the container bean The configuration class takes effect , Reinjection MybatisAutoConfiguration
example , In fact, it does not take effect under normal circumstances , When did this take effect ? At startup , You need to add an annotation @MapperScan(""), Packet scan path , The annotation will be sent to IOC register MapperScannerRegistrar
// Profile import MybatisAutoConfiguration And AutoConfiguredMapperScannerRegistrar
// When there is no MapperFactoryBean.class, MapperScannerConfigurer.class @Configuration
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.
class}) public static class MapperScannerRegistrarNotFoundConfiguration
implements InitializingBean { public MapperScannerRegistrarNotFoundConfiguration
() { } public void afterPropertiesSet() { MybatisAutoConfiguration.logger.debug(
"Not found configuration for registering mapper bean using @MapperScan,
MapperFactoryBean and MapperScannerConfigurer."); } }
@MapperScan Will introduce MapperScannerRegistrar
springboot Automatic assembly process
1.mybatis-spring-boot-autoconfigure-2.1.2.jar=》spring.factories Scan in file
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
2. complete MybatisAutoConfiguration In the configuration file bean instantiation , stay IOC Create in container SqlSessionFactory And SqlSessionTemplate example
Technology