Mybatis源码1.0
前言
Mybatis源码理解记录
时隔2年左右,再次开启博客记录。今天是开始记录SpringBoot整合Mybatis这个框架的个人理解。
Mybatis初始化
spring.factories
mybatis-spring-boot-autoconfigure.jar中是mybatis初始的所使用的jar。主要通过spring.factories。spring.factories工厂文件通过@SpringBootApplication注解 -> @EnableAutoConfiguration注解 -> @Import(AutoConfigurationImportSelector.class)
而在AutoConfigurationImportSelector这个类中,getCandidateConfigurations方法 protected ListgetCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you " + "are using a custom packaging, make sure that file is correct."); return configurations;} 这个方法会加载所有引入的JAR包中下META-INF/spring.factories的工厂文件。
在mybatis-spring-boot-autoconfigure.jar中所加载的class通过EnableAutoConfiguration加载到Spring容器中
MybatisAutoConfiguration.class
我们主要关注的Class就是MybatisAutoConfiguration
在这个Class初始化的时候会将MybatisProperties加载进来,而MybatisProperties这里面声明的就是我们在application.yml中所配置的mybatis参数
在MybatisAutoConfiguration中还存在两个静态内部类,
(MapperScannerRegistrarNotFoundConfiguration、AutoConfiguredMapperScannerRegistrar)在MapperScannerRegistrarNotFoundConfiguration也同样配置了@Import以及@ConditionalOnMissingBean注解
AutoConfiguredMapperScannerRegistrar 实现了ImportBeanDefinitionRegistrar;
所以,spring在refresh的时候,会执行这个类的 registerBeanDefinitions()方法,将 MapperScannerConfigurer存到了beanDefinitionMap中MapperScannerConfigurer是BeanDefinitionRegistryPostProcessor的实现类,在refresh() –> invokeBeanFactoryPostProcessors(beanFactory)中,会遍历所有beanFactoryPostProcessor和BeanDefinitionRegistrtPostProcessor的实现类,依次执行postProcessorBeanDefinitionRegistrar()方法
在将mapper扫描完之后,需要进行sql的解析,在和springboot整合之后,需要在配置文件中配置当前要扫描的mapper.xml文件,mybatis.mapperLocations=classpath:mapper/*Mapper.xml
这里的mapperLocation,是在sqlSesionFactorybean中进行解析的,在第3步中的自动配置类中,通过@Bean,注入了SqlSessionFactory,
在sqlSessionFactory()方法最后,会调用factoryBean.getObject()方法,这里其实调用的就是SqlSessionFactory的getObject()方法@Override public SqlSessionFactory getObject() throws Exception { if (this.sqlSessionFactory == null) { afterPropertiesSet(); } return this.sqlSessionFactory; }
从afterPropertiesSet()方法,一直往下追,会追到同类中的buildSqlSessionFactory()方法,在这个方法中,判断如果当前mapperlocation不为null,就进行解析
if (this.mapperLocations != null) { if (this.mapperLocations.length == 0) { LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found."); } else { for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'"); } } }
在xmlMapperBuilder.parse()就是原生mybatis在解析xml文件时,需要调用的方法
在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