SpringBoot与Mybatis整合方式01(源码分析)
前言:入职新公司,SpringBoot和Mybatis都被封装了一次,光用而不知道原理实在受不了,于是开始恶补源码,由于刚开始比较浅,存属娱乐,大神勿喷。
就如网上的流传的SpringBoot与Mybatis整合两种方式
一.使用 pom文件使用:org.mybatis.spring.boot 依赖
坐标:
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>xxxxx</version>
通过pom.xml能够看到该依赖的其他依赖文件
注意这个org.mybatis.spring....autoconfigure包,找到这个包,点开META-INF目录下,找到spring.factories文件 这是自动配置mybatis注解,mapper存放位置等的地方。
这里如果EnableAutoConfigurationImportSelector不懂的可以看 https://www.jianshu.com/p/464d04c36fb1
,这里不在详细解释,只要知道是Spring的一个自动配置的注解,通过它会自动加载类。
进入 MybatisAutoConfiguration
@Configuration @ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) @ConditionalOnBean({DataSource.class}) @EnableConfigurationProperties({MybatisProperties.class}) @AutoConfigureAfter({DataSourceAutoConfiguration.class}) public class MybatisAutoConfiguration { private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class); private final MybatisProperties properties; private final Interceptor[] interceptors; private final ResourceLoader resourceLoader; private final DatabaseIdProvider databaseIdProvider; private final List<ConfigurationCustomizer>configurationCustomizers;
MybatisProperties 类是接受Mybatis配置文件的类
@ConfigurationProperties( prefix = "mybatis" ) public class MybatisProperties { public static final String MYBATIS_PREFIX = "mybatis"; private String configLocation; private String[] mapperLocations; private String typeAliasesPackage; private String typeHandlersPackage; private boolean checkConfigLocation = false; private ExecutorType executorType; private Properties configurationProperties; @NestedConfigurationProperty private Configuration configuration;
config-location Location of MyBatis xml config file.
check-config-location Indicates whether perform presence check of the MyBatis xml config file.
mapper-locations Locations of Mapper xml config file.
type-aliases-package Packages to search for type aliases. (Package delimiters are ",; \t\n")
type-handlers-package Packages to search for type handlers. (Package delimiters are ",; \t\n")
executor-type Executor type: SIMPLE, REUSE, BATCH.
configuration-properties Externalized properties for MyBatis configuration. Specified properties can be used as placeholder on MyBatis config file and Mapper file. For detail see the MyBatis reference page
configuration A MyBatis Configuration bean. About available properties see the MyBatis reference page. NOTE This property cannot be used at the same time with the config-location.
@PostConstruct 在初始化的时候执行:加载配置文件
@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)"); } @Bea@ConditionalOnMissingBean 自动创建 sqlSessionFactory和SqlSessionTemplate
下面的静态内部类 AutoConfiguredMapperScannerRegistrar 会在运行期间@Mapper注解,使用过@Mapper的Mapper接口
自动的生成动态代理mapper接口
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware { private BeanFactory beanFactory; private ResourceLoader resourceLoader; public AutoConfiguredMapperScannerRegistrar() { } public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper"); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); try { if (this.resourceLoader != null) { scanner.setResourceLoader(this.resourceLoader); } List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (MybatisAutoConfiguration.logger.isDebugEnabled()) { Iterator var5 = packages.iterator(); while(var5.hasNext()) { String pkg = (String)var5.next(); MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg); } } //注册注解 scanner.setAnnotationClass(Mapper.class); scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(packages)); } catch (IllegalStateException var7) { MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", var7); } } public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } }
注意:要是此时在SpringBoot启动类上使用了@MapperScan()注解,那么此时将进入@Import 导入的类,进行注册
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) @Documented @Import({MapperScannerRegistrar.class}) public @interface MapperScan { String[] value() default {}; String[] basePackages() default {}; Class<?>[] basePackageClasses() default {}; Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class; Class<? extends Annotation> annotationClass() default Annotation.class; Class<?> markerInterface() default Class.class; String sqlSessionTemplateRef() default ""; String sqlSessionFactoryRef() default ""; Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class; }
在 MapperScannerRegistrar的 registerBeanDefinitions 方法用ClassPathMapperScanner进行注册,使用doScan扫描包
此时注册的注解需要annotationClass的值,
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName())); ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); if (this.resourceLoader != null) { scanner.setResourceLoader(this.resourceLoader); } Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass"); if (!Annotation.class.equals(annotationClass)) { scanner.setAnnotationClass(annotationClass); } Class<?> markerInterface = annoAttrs.getClass("markerInterface"); if (!Class.class.equals(markerInterface)) { scanner.setMarkerInterface(markerInterface); } Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator"); if (!BeanNameGenerator.class.equals(generatorClass)) { scanner.setBeanNameGenerator((BeanNameGenerator)BeanUtils.instantiateClass(generatorClass)); } Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean"); if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) { scanner.setMapperFactoryBean((MapperFactoryBean)BeanUtils.instantiateClass(mapperFactoryBeanClass)); } scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef")); scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef")); List<String> basePackages = new ArrayList(); String[] var10 = annoAttrs.getStringArray("value"); int var11 = var10.length; int var12; String pkg; for(var12 = 0; var12 < var11; ++var12) { pkg = var10[var12]; if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } var10 = annoAttrs.getStringArray("basePackages"); var11 = var10.length; for(var12 = 0; var12 < var11; ++var12) { pkg = var10[var12]; if (StringUtils.hasText(pkg)) { basePackages.add(pkg); } } Class[] var14 = annoAttrs.getClassArray("basePackageClasses"); var11 = var14.length; for(var12 = 0; var12 < var11; ++var12) { Class<?> clazz = var14[var12]; basePackages.add(ClassUtils.getPackageName(clazz)); } scanner.registerFilters(); scanner.doScan(StringUtils.toStringArray(basePackages)); }
若既不使用@MapperScan 注解 也不使用@Mapper注解,则Mapper类不能生成代理对象,则在@Autowire注入时,会报错
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException
二 使用Mybatis-Spring整合
该整合jar包含的类
spring.handlers中内容找到处理器org.mybatis.spring.config.NamespaceHandler
http\://mybatis.org/schema/mybatis-spring=org.mybatis.spring.config.NamespaceHandler
处理器中会有解析器MapperScannerBeanDefinitionParser来解析这个xml
public void init() { this.registerBeanDefinitionParser("scan", new MapperScannerBeanDefinitionParser()); }
在 MapperScannerBeanDefinitionParser进行注册
public synchronized BeanDefinition parse(Element element, ParserContext parserContext) { ClassPathMapperScanner scanner = new ClassPathMapperScanner(parserContext.getRegistry()); ClassLoader classLoader = scanner.getResourceLoader().getClassLoader(); XmlReaderContext readerContext = parserContext.getReaderContext(); scanner.setResourceLoader(readerContext.getResourceLoader()); String annotationClassName; String markerInterfaceClassName; String nameGeneratorClassName; try { annotationClassName = element.getAttribute("annotation"); if (StringUtils.hasText(annotationClassName)) { Class<? extends Annotation> markerInterface = classLoader.loadClass(annotationClassName); scanner.setAnnotationClass(markerInterface); } markerInterfaceClassName = element.getAttribute("marker-interface"); if (StringUtils.hasText(markerInterfaceClassName)) { Class<?> markerInterface = classLoader.loadClass(markerInterfaceClassName); scanner.setMarkerInterface(markerInterface); } nameGeneratorClassName = element.getAttribute("name-generator"); if (StringUtils.hasText(nameGeneratorClassName)) { Class<?> nameGeneratorClass = classLoader.loadClass(nameGeneratorClassName); BeanNameGenerator nameGenerator = (BeanNameGenerator)BeanUtils.instantiateClass(nameGeneratorClass, BeanNameGenerator.class); scanner.setBeanNameGenerator(nameGenerator); } } catch (Exception var11) { readerContext.error(var11.getMessage(), readerContext.extractSource(element), var11.getCause()); } annotationClassName = element.getAttribute("template-ref"); scanner.setSqlSessionTemplateBeanName(annotationClassName); markerInterfaceClassName = element.getAttribute("factory-ref"); scanner.setSqlSessionFactoryBeanName(markerInterfaceClassName); scanner.registerFilters(); nameGeneratorClassName = element.getAttribute("base-package"); scanner.scan(StringUtils.tokenizeToStringArray(nameGeneratorClassName, ",; \t\n")); return null; }
也可以使用@MapperScanner注解 在 MapperScannerConfigurer进行注册
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.registerFilters(); scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n")); }
还可以在 xml文档里注册 MapperScannerConfigurer自动管理(同上,一个xml一个注解)
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.mybatis.spring.sample.mapper" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
注意: org-mybatis-spring.boot为: 1.3.0 mybatis-spring:1.3.1 ,mybatis:3.4.4