我們接下來說:springboot是如何和mybatis進行整合的
1.首先,springboot中使用mybatis需要用到mybatis-spring-boot-start,可以理解為mybatis開發的整合springboot的jar包
有一個關鍵點先說明:前面也提到過,不管是mybatis和spring整合,還是和springboot整合,都需要做兩個操作:
1.把當前接口和對應的mapperProxyFactory存入到knownMappers中,
2.把sql包裝成mappedStatement,存入到mappedStatements這個map中
1.springboot項目中,@SpringbootApplication注解上,有一個@EnableAutoConfiguration注解,而@EnableAutoConfiguration注解又利用了@Import注解,注入了一個ImportSelector的實現類 AutoConfigurationImportSelector.class
2.AutoConfigurationImportSelector在selectImports()方法中有一行重要的代碼:
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
這行代碼內部,會從所有jar包中,META-INF/spring.factories文件中,加載EnableAutoConfiguration對應的實現類,那mybatis-spring-boot-autoconfigure.jar包中,配置了兩個實現類,
1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\ 3 org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
3.我們來說 MybatisAutoConfiguration.class
1 @org.springframework.context.annotation.Configuration 2 @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class }) 3 @ConditionalOnSingleCandidate(DataSource.class) 4 @EnableConfigurationProperties(MybatisProperties.class) 5 @AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class }) 6 public class MybatisAutoConfiguration implements InitializingBean { 7 8 9 }
這里有一個點,就是 @EnableConfigurationProperties(MybatisProperties.class);點開MybatisProperties.class文件會發現,這里面聲明的就是,在application.properties配置文件中,mybatis提供的配置信息:比如mybatis.mapper-locations=classpath:mapping/*Mapper.xml;這個點,不細說了,后面可能會寫一篇自定義starter的學習筆記,到時候 再詳細寫
4.在MybatisAutoConfiguration中有一個靜態內部類 AutoConfiguredMapperScannerRegistrar 實現了ImportBeanDefinitionRegistrar;
所以,spring在refresh的時候,會執行這個類的 registerBeanDefinitions()方法,將 MapperScannerConfigurer存到了beanDefinitionMap中
1 @Override 2 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 3 4 5 if (!AutoConfigurationPackages.has(this.beanFactory)) { 6 logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); 7 return; 8 } 9 //中間刪除了部分代碼 10 registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition()); 11 }
5.MapperScannerConfigurer是BeanDefinitionRegistryPostProcessor的實現類,在refresh() --> invokeBeanFactoryPostProcessors(beanFactory)中,會遍歷所有beanFactoryPostProcessor和BeanDefinitionRegistrtPostProcessor的實現類,依次執行postProcessorBeanDefinitionRegistrar()方法
MapperScannerConfigurer的postProcessBeanDefinitionRegistry()方法,會執行掃描方法,這里的掃描方法,和mapperScannerRegistrar的registerBeanDefinitions中的doScan是一樣的,這里掃描的包是在,初始化MapperScannerConfigurer的時候,在執行完屬性注入之后,調用了截圖中的方法,把當前pvs中的basePackage傳到MapperScannerConfigurer中,這里是如何傳過去的,待研究
6.在將mapper掃描完之后,需要進行sql的解析,在和springboot整合之后,需要在配置文件中配置當前要掃描的mapper.xml文件,
mybatis.mapper-locations=classpath:mapping/*Mapper.xml
這里的mapperLocation,是在sqlSesionFactorybean中進行解析的,在第3步中的自動配置類中,通過@Bean,注入了SqlSessionFactory,
在sqlSessionFactory()方法最后,會調用factoryBean.getObject()方法,這里其實調用的就是SqlSessionFactory的getObject()方法,
1 @Override 2 public SqlSessionFactory getObject() throws Exception { 3 if (this.sqlSessionFactory == null) { 4 afterPropertiesSet(); 5 } 6 7 8 return this.sqlSessionFactory; 9 }
從afterPropertiesSet()方法,一直往下追,會追到同類中的buildSqlSessionFactory()方法,在這個方法中,判斷如果當前mapperlocation不為null,就進行解析
1 if (this.mapperLocations != null) { 2 if (this.mapperLocations.length == 0) { 3 LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found."); 4 } else { 5 for (Resource mapperLocation : this.mapperLocations) { 6 if (mapperLocation == null) { 7 continue; 8 } 9 try { 10 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), 11 targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments()); 12 xmlMapperBuilder.parse(); 13 } catch (Exception e) { 14 throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); 15 } finally { 16 ErrorContext.instance().reset(); 17 } 18 LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'"); 19 } 20 } 21 }
在xmlMapperBuilder.parse()就是原生mybatis在解析xml文件時,需要調用的方法
7.在service中注入mapper接口,在初始化service,注入依賴的mapper接口時,還是調用的mapperFactorybean.getObject()方法來獲取代理對象
springboot整合mybatis 和 spring+mybatis整合時,解析xml文件有一個區別:
spring-mybatis是利用mapperFactorybean的checkDao()方法來解析xml,put數據到mappedStatement和knowmappers
springboot是利用SqlSessionFactoryBean的getObject()來解析xml,put數據到mappedStatement和knowMappers