MyBaits自動配置原理

前言

首先我們建立一個SpringBoot工程,導入mybatis-spring-boot-starter依賴。

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.4</version>
</dependency>

導入後發現這個依賴其實就是幫助我們導入了mybatis需要的依賴,其中和自動配置相關最重要的一個就是mybatis-spring-boot-autoconfigure

<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>

MyBatis自動配置中是如何工作的

如上面分析自動配置的關鍵類我們就從mybatis-spring-boot-autoconfigure開始著手分析。

QQ截圖20210517172159.png

spring.factories

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

從spring.factories文件看到這裡通過SPI機制載入了兩個類

  • MybatisAutoConfiguration
  • MybatisLanguateDriverAutoConfiguration.

MybatisAutoConfiguration

QQ截圖20210517172514.png

//表示這是一個Spring配置類
@Configuration 
//這個類需要在classpath中存在SqlSessionFactory和SqlSessionFactoryBean時才生效
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) --
//這個類需要有一個DataSource的Canidate註冊到Spring容器中 
@ConditionalOnSingleCandidate(DataSource.class)
//使MybatisProperties註解類生效
@EnableConfigurationProperties({MybatisProperties.class})
//需要在DataSourceAutoConfiguration和MybatisLanguageDriverAutoConfiguration自動配置之後執行
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class})
public class MybatisAutoConfiguration implements InitializingBean {
   
}

MybatisAutoConfiguration#sqlSessionFactory

@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
	//創建一個SqlSessionFactoryBean, 在mybatis-spring項目下
    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對象
	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);
	}
  
    //這裡默認會返回一個DefaultSqlSessionFactory對象
	return factory.getObject();
}

MybatisAutoConfiguration#sqlSessionTemplate

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
	ExecutorType executorType = this.properties.getExecutorType();
	return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);
}

到這裡也就知道了MyBatis自動配置其實就是替我們完成了SqlSessionFactory和SqlSessionTempate的創建, 省去了自己導入相關依賴和配置相關Bean的麻煩.

MybatisLanguageDriverAutoConfiguration

這個類的配置是對各個語言的支援,比如Thymeleaf, Velocity,LegacyVelociy, FreeMarker等視圖組件的支援。

@Configuration
@ConditionalOnClass({LanguageDriver.class})
public class MybatisLanguageDriverAutoConfiguration {
    private static final String CONFIGURATION_PROPERTY_PREFIX = "mybatis.scripting-language-driver";

    public MybatisLanguageDriverAutoConfiguration() {
    }

    @Configuration
    @ConditionalOnClass({ThymeleafLanguageDriver.class})
    public static class ThymeleafConfiguration {
        public ThymeleafConfiguration() {
        }
    }

    @Configuration
    @ConditionalOnClass({VelocityLanguageDriver.class, VelocityLanguageDriverConfig.class})
    public static class VelocityConfiguration {
        public VelocityConfiguration() {
        }
    }

    @Configuration
    @ConditionalOnClass({Driver.class})
         @ConditionalOnMissingClass({"org.mybatis.scripting.velocity.VelocityLanguageDriverConfig"})
    public static class LegacyVelocityConfiguration {
        public LegacyVelocityConfiguration() {
        }
    }

    @Configuration
    @ConditionalOnClass({FreeMarkerLanguageDriver.class, FreeMarkerLanguageDriverConfig.class})
    public static class FreeMarkerConfiguration {
        public FreeMarkerConfiguration() {
        }
    }

    @Configuration
    @ConditionalOnClass({FreeMarkerLanguageDriver.class})
    @ConditionalOnMissingClass({"org.mybatis.scripting.freemarker.FreeMarkerLanguageDriverConfig"})
    public static class LegacyFreeMarkerConfiguration {
        public LegacyFreeMarkerConfiguration() {
        }
    }
}

MybatisLanguageDriverAutoConfiguration類在org.mybatis.spring.boot.autoconfigure包下,我刪掉了內部靜態類下的程式碼,為了保持這個類看起來更直觀

自定義Mapper是如何被掃描的

業務開發中,我們是聲明介面(Mapper),那麼我們自定義的Mapper是如何被掃描的呢, 我們繼續順著MybatisAutoConfiguration程式碼分析,其內部包含了一個AutoConfiguredMapperScannerRegistrar的內部靜態類.

AutoConfiguredMapperScannerRegistrar

registerBeanDefinitions

public static class AutoConfiguredMapperScannerRegistrar 
    implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
	private BeanFactory beanFactory;

	public AutoConfiguredMapperScannerRegistrar() {
	}

	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		if (!AutoConfigurationPackages.has(this.beanFactory)) {
			
		} else {
	        //1.獲取到SpringBoot的基礎包路徑
			List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
			
            //2.生成一個BeanDefinition的構造器,用於構建MapperScannerConfigurer的                         //BeanDefinition
			BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
			builder.addPropertyValue("processPropertyPlaceHolders", true);
            //3.設置@Mapper註解的介面才會被當成Mapper介面
			builder.addPropertyValue("annotationClass", Mapper.class);
            
			builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
			BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
			//4.獲取MapperScannerConfigurer的屬性名稱
            Set<String> propertyNames = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
			if (propertyNames.contains("lazyInitialization")) {
				builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
			}

			if (propertyNames.contains("defaultScope")) {
				builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
			}
            //5.這裡添加一個MapperScannerConfigurer的BeanDefinition對象,也就是注入一個
            //MapperScannerConfigurer對象
			registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
		}
	}

	public void setBeanFactory(BeanFactory beanFactory) {
		this.beanFactory = beanFactory;
	}
}

AutoConfiguredMapperScannerRegistrar類是MybatisAutoConfiguration的內部靜態類,位於包org.mybatis.spring.boot.autoconfigure下。

可以看到這個類實現了ImportBeanDefinitionRegistrar介面,ImportBeanDefinitionRegistrar介面是Spring用來動態註冊Bean的,也就是會向Spring容器中注入一個BeanDefinition, 這個BeanDefinition就是MapperScannerConfigurer。
ImportBeanDefinitionRegistrar實現類只能通過其他類@Import的方式來載入,通常是配置類或者啟動類,所以MybatisAutoConfiguration類下還有一個內部類MapperScannerRegistrarNotFoundConfiguration如下。

@Configuration
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
        
}

這個方法里最後會調用介面BeanDefinitionRegistry.registerBeanDefinition, beanName是”org.mybatis.spring.mapper.MapperScannerConfigurer”, registerBeanDefinition方法實際會調用DefaultListableBeanFactory.registerBeanDefinition。DefaultListableBeanFactory是BeanDefinitionRegistry介面的實現類。
QQ截圖20210518113732.png

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory 
    implements ConfigurableListableBeanFactory, 
               BeanDefinitionRegistry, 
               Serializable {

}

AutoConfiguredMapperScannerRegistrar類和MapperScanner註解的作用是一樣的,如果你沒有通過以下三種配置方式掃描Mapper介面的包路徑

  • 配置MapperScannerConfigurer掃描器類型的Spring Bean
  • @Mapper註解
  • <mybatis: scan/>標籤

那麼這裡就會通過AutoConfiguredMapperScannerRegistrar類添加一個MapperScannerConfigurer掃描器對象,去掃描SpringBoot包設置的基礎包路徑,也就是啟動類的同級目錄。 如果設置了@Mapper註解,則會當成Mapper介面解析,那麼這裡自動配置則不生效。

MapperScannerConfigurer
QQ截圖20210517175252.png

MapperScannerConfigurer

MapperScannerConfigurer實現了BeanDefinitionRegistryPostProcessor介面,BeanDefinitionRegistryPostProcessor 介面又繼承了BeanFactoryPostProcessor介面, 也就是說在MapperScannerConfigurer類里需要實現這兩個介面的方法。

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}

@FunctionalInterface
public interface BeanFactoryPostProcessor {
    void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}

在MapperScannerConfigurer類里可以看到這裡只實現了postProcessBeanDefinitionRegistry。

QQ截圖20210521091303.png

BeanDefinitionRegistryPostProcessor

Spring里有兩個用來動態註冊Bean到容器中(BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar)。ImportBeanDefinitionRegistrar上文中有提到。
BeanDefinitionRegistryPostProcessor介面實現了BeanFactoryPostProcessor介面,是Spring框架的BeanDefinitionRegistry的後置處理器,用來註冊額外的BeanDefinition,postProcessBeanDefinitionRegistry方法會在所有的beanDefinition被載入了,但是所有的Bean還沒創建前調用。BeanDefinitionRegistryPostProcessor經常被用來註冊BeanFactoryPostProcessor的BeanDefinition。

postProcessBeanDefinitionRegistry

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
            this.processPropertyPlaceHolders();
        }

        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(this.lazyInitialization)) {
            scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
        }

        if (StringUtils.hasText(this.defaultScope)) {
            scanner.setDefaultScope(this.defaultScope);
        }

        scanner.registerFilters();
        scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
    }
}

MapperScannerConfigurer在包org.mybatis.spring.mapper下.

這裡會調用ClassPathMapperScanner.scan(),而ClassPathMapperScanner又繼承了ClassPathBeanDefinitionScanner,所以這裡scan()會調用ClassPathBeanDefinitionScanner.scan(), 而ClassPathBeanDefinitionScanner.scan() 第二句程式碼又調用了this.doScan(basePackages), this.doScan()又調用了ClassPathMapperScanner.doScan(), 而這個方法第一句程式碼又調用了super.doScan(basePackages),父子類來回互相調用,有點暈頭轉向的。

org.mybatis.spring.mapper.ClassPathMapperScanner
org.springframework.context.annotation.ClassPathBeanDefinitionScanner這個類在spring-context.jar

ClassPathMapperScanner

ClassPathBeanDefinitionScanner#scan

public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    this.doScan(basePackages);
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return this.registry.getBeanDefinitionCount() - beanCountAtScanStart;
}

ClassPathMapperScanner#doScan()
這個方法里在mybatis自動配置算比較重要的一個方法,也就是幫助我們自動配置MapperFactoryBean, 會把根據basePackage註冊進Spring容器的BeanDefinition的beanClass設置成MapperFactoryBean。

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
        LOGGER.warn(() -> {
            return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
        });
    } else {
        //這是一個關鍵方法,會處理已經註冊進Spring容器的beanDefinition,也就是會把
        //已經註冊進Spring容器的beanDefinitiond的beanClass為MapperFactoryBean
        this.processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}

ClassPathBeanDefinitionScanner#doScan()

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet();
    String[] var3 = basePackages;
    int var4 = basePackages.length;
    
    for(int var5 = 0; var5 < var4; ++var5) {
        String basePackage = var3[var5];
        //這個方法會掃描指定basePackage下被@Mapper註解標註的介面
        Set<BeanDefinition> candidates = this.findCandidateComponents(basePackage);
        Iterator var8 = candidates.iterator();

        while(var8.hasNext()) {
            BeanDefinition candidate = (BeanDefinition)var8.next();
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            //這裡獲取beanName, 默認值是類名首字母小寫
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                this.postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName);
            }

            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition)candidate);
            }
            //檢查對應的Mapper介面是否被註冊進Spring容器中。
            if (this.checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                this.registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    
    //這個集合返回以後 Spring容器會將裡面的所有內容註冊到容器中
    return beanDefinitions;
}

ClassPathMapperScanner#processBeanDefinitions

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
	BeanDefinitionRegistry registry = this.getRegistry();
	Iterator var4 = beanDefinitions.iterator();

	while(var4.hasNext()) {
		BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next();
		AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
		boolean scopedProxy = false;
		if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
			definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> {
				return new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]");
			});
			scopedProxy = true;
		}

		String beanClassName = definition.getBeanClassName();
		LOGGER.debug(() -> {
			return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface";
		});
		definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
		//1.這裡把普通介面設置成MapperFactoryBean
		definition.setBeanClass(this.mapperFactoryBeanClass);
        //2.是否把Mapper介面加入到Mybatis的Config當中去, 這裡設置為true
		definition.getPropertyValues().add("addToConfig", this.addToConfig);
		definition.setAttribute("factoryBeanObjectType", beanClassName);
		boolean explicitFactoryUsed = false;
		
		//2.從核心容器里獲取SqlSessionFactory賦值給MapperFactoryBean
		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;
		}
		
		//3.從核心容器里獲取SqlSessionTemplate賦值給MapperFactoryBean
		if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
			if (explicitFactoryUsed) {
				LOGGER.warn(() -> {
					return "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(() -> {
					return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.";
				});
			}

			definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
			explicitFactoryUsed = true;
		}

		if (!explicitFactoryUsed) {
			LOGGER.debug(() -> {
				return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.";
			});
			definition.setAutowireMode(2);
		}

		definition.setLazyInit(this.lazyInitialization);
		if (!scopedProxy) {
			if ("singleton".equals(definition.getScope()) && this.defaultScope != null) {
				definition.setScope(this.defaultScope);
			}

			if (!definition.isSingleton()) {
				BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
				if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
					registry.removeBeanDefinition(proxyHolder.getBeanName());
				}

				registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
			}
		}
	}
}

QQ截圖20210520174957.png

再看spring.factories文件

上面只是完成了MyBatis 自動配置的工作,那麼這些自動配置是如何在SpringBoot啟動時執行呢? 還記得SpringBoot的EnableAutoConfiguration註解吧,它是利用SpringFactoriesLoader機制載入所有的AutoConfiguration類,所以我們需要把編寫好的自動配置類放在META-INF/spring.factories文件中,這也就是我們一開始分析的mybatis-spring-boot-starter-autoconfigure的文件結構的原因。

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

總結

  1. mybatis-spring-boot-starter將mybatis需要的依賴全部引入。
  2. starter通過SPI機制引入了一個配置的Class(MyBatisAutoConfiguration)。它負責註冊SqlSessionFactory和SqlSessionTemplate到Spring容器中,使用MyBatis開發時絕大部分功能要使用這兩個類來完成。
  3. 注入了AutoConfiguredMapperScannerRegistrar這個Bean到Spring容器,它負責將MapperScanner引入到Spring容器,然後MapperScanner會將工程中指定package下的Mapper轉化為BeanDefinition並且註冊到Spring容器中。
  4. 在開發中使用某個具體的Mapper時,Spring能夠從容器中找到這個Mapper對應的BeanDefinition,將其實例化並且注入,這樣開發者就可以使用了。
  5. SpringBoot+MyBatis自動配置涉及到Spring中兩個自動註冊Bean的關鍵介面(BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar),也是我們業務開發中需要重點關注的地方。

參考鏈接

//www.cnblogs.com/nullifier/p/11967659.html
//zhuanlan.zhihu.com/p/30123517
//www.cnblogs.com/daxin/p/3545040.html