springboot实战电商项目mall4j (https://gitee.com/gz-yami/mall4j)
代码版本
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
既然是springboot 那么要想知道怎么运行的,有从三个地方入手
xxxAutoConfiguration
- yml对应的配置文件(
xxxProperties
) - 配置的注解
我们先来看三个类
1. MybatisAutoConfiguration 构造SqlSession
这个类通过dataSource
的配置,构造出SqlSessionFactory
与 SqlSessionTemplate
。如果以前使用spring相关的api的话,应该会比较熟悉 jdbcTemplate
与 redisTemplate
之类的。SqlSessionTemplate
这个命名,就会让人联想到这个也是类似的功能。而session
这个词很明显就是与服务之间交互保存连接状态的东西。Factory是工厂模式。从而可以得出:SqlSessionFactory
是用来创建SqlSession
的,SqlSession
可以打开或关闭与数据库的连接。SqlSessionTemplate
就是操作这些开关的关键。
@EnableConfigurationProperties(MybatisProperties.class)
public class MybatisAutoConfiguration implements InitializingBean {
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
// 省略...
}
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
// 省略...
}
}
2. MybatisProperties 读取配置信息
这个类主要是将配置文件里面的配置转成bean映射,配置文件类似这个样子
#mybatis的相关配置
mybatis:
#mapper配置文件
mapper-locations: classpath:mapper/*Mapper.xml
type-aliases-package: com.frozen-watermelon.**.model
#开启驼峰命名
configuration:
map-underscore-to-camel-case: true
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
}
3. @MapperScan 扫描Mapper接口
我们通常为了确定被扫描的mapper所属的包,都会有这样一个配置
@Configuration
@MapperScan({
"com.frozen-watermelon.**.mapper" })
public class MybatisConfig {
}
这里就有这个注解@MapperScan
,那么这个注解是如何在源码中运用的呢?
我们先看下这个注解类
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
String[] value() default {
};
String[] basePackages() default {
};
}
这里面@Import
了 MapperScannerRegistrar.class
,也就是说这个类被实例化了。这个类同时实现了ImportBeanDefinitionRegistrar
接口,也就是说 registerBeanDefinitions()
这个方法会被调用
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 获取@MapperScan的配置信息
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
// 准备构建MapperScannerConfigurer
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
// 省略...
List<String> basePackages = new ArrayList<>();
// 获取注解的配置的信息,
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
// 构建对象
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}
从上面的代码可以看出最终构建了一个MapperScannerConfigurer
对象。那么MapperScannerConfigurer
到底是干嘛用的呢?MapperScannerConfigurer
实现了BeanDefinitionRegistryPostProcessor
接口,也就是说postProcessBeanDefinitionRegistry()
这个方法创建完bean之后会被调用
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 省略...
// 扫描basePackage
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
4. 创建mapper代理对象
我们来看下扫描干了啥