首先我们建立一个SpringBoot工程,然后导入mybatis-spring-boot-starter依赖
导入后发现这个依赖其实就是帮助我们导入了mybatis需要的依赖,其中和自动配置相关最重要的一个就是mybatis-spring-boot-autoconfigure
MyBatis自动配置中是如何工作的
如上面分析自动配置的关键类我们就从mybatis-spring-boot-autoconfigure开始着手分析。
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
//表示这是一个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
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
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接口的实现类。
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable { }
AutoConfiguredMapperScannerRegistrar类和MapperScanner注解的作用是一样的,如果你没有通过以下三种配置方式扫描Mapper接口的包路径
配置MapperScannerConfigurer扫描器类型的Spring Bean
@Mapper注解
那么这里就会通过AutoConfiguredMapperScannerRegistrar类添加一个MapperScannerConfigurer扫描器对象,去扫描SpringBoot包设置的基础包路径,也就是启动类的同级目录。 如果设置了@Mapper注解,则会当成Mapper接口解析,那么这里自动配置则不生效。
MapperScannerConfigurer
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。
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
ClassPathBeanDefinitionScanner#doScan()
protected Set
ClassPathMapperScanner#processBeanDefinitions
private void processBeanDefinitions(Set
上面只是完成了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
总结
mybatis-spring-boot-starter将mybatis需要的依赖全部引入。
starter通过SPI机制引入了一个配置的Class(MyBatisAutoConfiguration)。它负责注册SqlSessionFactory和SqlSessionTemplate到Spring容器中,使用MyBatis开发时绝大部分功能要使用这两个类来完成。
注入了AutoConfiguredMapperScannerRegistrar这个Bean到Spring容器,它负责将MapperScanner引入到Spring容器,然后MapperScanner会将工程中指定package下的Mapper转化为BeanDefinition并且注册到Spring容器中。
在开发中使用某个具体的Mapper时,Spring能够从容器中找到这个Mapper对应的BeanDefinition,将其实例化并且注入,这样开发者就可以使用了。
SpringBoot+MyBatis自动配置涉及到Spring中两个自动注册Bean的关键接口(BeanDefinitionRegistryPostProcessor和ImportBeanDefinitionRegistrar),也是我们业务开发中需要重点关注的地方。
还有不懂的小伙伴可以添加作者的联系方式
QQ:1162798594
vx:tan1999nn
喜欢的小伙伴可以给作者我点点赞,点点关注哈~~~