学习源码过程中随手记录的笔记,仅供参考,有问题欢迎指出交流
可能比较枯燥,耐点心,但是弄懂了,必能知其然而知其所以然
学习源码建议亲手debug调试
使用的源码版本
mybatis版本3.5.3
spring版本5.2.0
测试代码示例
@Configuration
@MapperScan("com.cheng.mapper")
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource());
// 设置 MyBatis 配置文件路径
//factoryBean.setConfigLocation(new ClassPathResource("mybatis/mybatis-config.xml"));
// 设置 SQL 映射文件路径
//factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/cheng/mapper/*.xml"));
//factoryBean.setTypeAliases(User.class);
return factoryBean;
}
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("xx");
dataSource.setPassword("xxx");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://xxx/mybatis_example?characterEncoding=utf8&useSSL=false");
return dataSource;
}
}
通过@MapperScan(“com.cheng.mapper”)注解进行切入分析
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {}
看到熟悉的@Import 其他三方与Spring无缝链接时,在零xml配置时,都是采用@Import注解进行处理器的注册
点进MapperScannerRegistrar这个类实现ImportBeanDefinitionRegistrar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {}
在bean定义扫描的时候会调用registerBeanDefinitions()方法往我们的容器中添加bean定义对象到 beanDefinitionMap中
AbstractApplicationContext#refresh#invokeBeanFactoryPostProcessors(beanFactory)
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
registrars.forEach((registrar, metadata) ->
registrar.registerBeanDefinitions(metadata, this.registry, this.importBeanNameGenerator));
}
看下MapperScannerRegistrar的registerBeanDefinitions()方法
先扫描@Mappscan注解
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
/**
* 从我们传入的配置类中来解析@MapperScan注解信息,然后吧MapperScan注解的属性转化为 AnnotationAttributes类型(Map类型)
*/
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
/**
* 若上一步解析出来的mapperScanAttrs不为空(说明配置类上加了@MapperScan注解)
*/
if (mapperScanAttrs != null) {
/**
* 调用重写的方法registerBeanDefinitions generateBaseBeanName(importingClassMetadata, 0) 我们即将注册的bean定义的名称
* com.cheng.config.MyBatisConfig#MapperScannerRegistrar#0
*/
registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
}
}
进入registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
这边对@MappScan注解属性的解析
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
/**
* 创建bean定义构造器 通过够构造器来构建出我们的bean定义<MapperScannerConfigurer> 应用到的设计模式[建造者模式]
*/
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
/**
* 手动为我们MapperScannerConfigurer 开启processPropertyPlaceHolders属性为true 我们需要着重研究下MapperScannerConfigurer类的继承结构
*/
builder.addPropertyValue("processPropertyPlaceHolders", true);
/**
* 为我们的MapperScannerConfigurer 解析我们@MapperScanner 指定扫描的的注解类型
*/
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
/**
* 是否配置了标记接口
*/
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
/**
* 设置MapperScannerConfigurer的beanName 生成器对象
*/
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
/**
* 解析@MapperScan注解属性MapperFactoryBean 设置到MapperScannerConfigurer 声明一个自定义的MapperFactoryBean 返回一个代理对象
*/
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
/**
* 解析@MapperScan 的sqlSessionTemplateRef到底使用是哪个sqlSessionTemplate 设置到MapperScannerConfigurer 多数据源的情况下需要指定
*/
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
/**
* 解析@MapperScan的sqlSessionFactoryRef属性 设置到 MapperScannerConfigurer 多数据情况下的话 ,需要指定使用哪个 sqlSessionFactory
*/
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
/**
* 解析@MapperScan 扫描的的包或者是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()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
/**
* 指定MapperScannerConfigurer 是否为懒加载
*/
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
/**
* 为我们的容器中注册了MapperScannerConfigurer的接口
*/
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
构建一个MapperScannerConfigurer,将@MapperScan的属性值解析包装到该类中
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
注册到beanDefinitionMap中
接下来看看MapperScannerConfigurer这个类实现了BeanDefinitionRegistryPostProcessor这个接口
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
}
那么就会在核心方法AbstractApplication#refresh()#invokeBeanFactoryPostProcessors(beanFactory)方法完成处理器的调用
private static void invokeBeanDefinitionRegistryPostProcessors(
Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessBeanDefinitionRegistry(registry);
}
}
上面后置处理方法执行时会进入MapperScannerConfigurer#postProcessBeanDefinitionRegistry()
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
/**
* 若MapperScannerConfigurer属性的processPropertyPlaceHolders 为ture的时候,就进行 processPropertyPlaceHolders();
*/
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
/**
* 显示的new 一个ClassPathMapperScanner 包扫描器对象 这个对象是mybaits继承了spring的 ClassPathBeanDefinitionScanner,有了扫描解析功能
* 为我们扫描器指定@MapperScan配置的包路径属性
*/
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(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
/**
* 扫描规则过滤
*/
scanner.registerFilters();
/**
* 真正的去扫描我们@MapperScan指定的路径下的bean定义信息 先会去调用ClassPathMapperScanner.scan()方法
*/
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
在这去扫描@MapperScan注解中填写的包名,加入到BeanDefinitionMap中
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
ClassPathMapperScanner#doscan
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
/**
* 调用父类ClassPathBeanDefinitionScanner 来进行扫描
*/
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
/**
* 若扫描后 我们mapper包下有接口类,那么扫描bean定义就不会为空
*/
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
+ "' package. Please check your configuration.");
} else {
/**
* mybaits在切入,将spring的 的bean定义玩到极致(做了偷天换日的操作) 现在我们知道t通过父类扫描出来的mapper是接口类型的
* 比如我们com.cheng.mapper.UserMapper 他是一个接口 可能会知道我们的bean定义最终会被实例化成
* 对象,但是我们接口是不能实例化的,所以在processBeanDefinitions 来进行偷天换日
*/
processBeanDefinitions(beanDefinitions);
}
ClassPathBeanDefinitionScanner#doScan
中会把@MapperScan配置的包路径进行扫描并注册BeanDefinitionMap中
到这,dao层的接口已经扫描完成并加入到beanDefinitionMap中了,此时还没有创建bean哦,不理解的看下spring源码(…)
还有一件事没有做,就是偷天换日,将dao接口转换成FactoryBean(为什么转换?..接口无法直接实例化啊)
简单过下FactoryBean概念
是一个工厂bean,实现该接口可以自定义要创建的bean,spring在创建bean的时候最终会调用它的getObject方法
回到上面processBeanDefinitions(beanDefinitions)
方法
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
/**
* 循环我们所有扫描出mapper的bean定义出来
*/
for (BeanDefinitionHolder holder : beanDefinitions) {
// 获取我们的bean定义
definition = (GenericBeanDefinition) holder.getBeanDefinition();
// 获取我们的bean定义的名称
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
/**
* 进行真的偷天换日操作,也就是这二行代码是最最最最最重要的, 关乎我们 spring整合mybaits的整合 definition.setBeanClass(this.mapperFactoryBeanClass);
*/
// 设置ConstructorArgumentValues 会通过构造器初始化对象
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
// 设置成factoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
/**
* 为我们的Mapper对象绑定我们的sqlSessionFactory引用 说白了就是我们的UserMapper(实际上是就是为我们的MapperFactoryBean添加一个sqlSessionFactory的属性)
* 然后SpringIoc在实例话我们的MapperFactoryBean的时候会经历populate()方法为我么你的UserMapper(MapperFactoryBean)
* 的sqlSessionFactory赋值(调用set方法)
*/
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
/**
* 为我们的Mapper对象绑定我们的sqlSessionTemplate属性对象
* 说白了就是我们的UserMapper(实际上是就是为我们的MapperFactoryBean添加一个sqlSessionTemplate的属性)
* 然后SpringIoc在实例话我们的MapperFactoryBean的时候会经历populate()方法为UserMapper(MapperFactoryBean)
* 的sqlSessionTemplate赋值(调用set方法)
*/
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
LOGGER.warn(
() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
// 将sqlSessionTemplate通过AUTOWIRE_BY_TYPE自动装配
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
/**
* 设置bean定义的加载模型(是否为懒加载)
*/
definition.setLazyInit(lazyInitialization);
}
}
将之前扫描的beanDefinitions进行循环
- 设置bean为MapperFactoryBean也就是FactoryBean
- 为MapperFactoryBean添加SqlSessionTempate属性
- 设置注入通过类型注入
至此,dao接口已经扫描完毕并加入到BeanDefinitionMap,并将beanClass设置成MapperFactoryBean了
期待下一篇吧
如果觉得还算凑合,麻烦点个赞哦