(七) mybatis 源码之 终章 : 与 Spring 整合

几乎所有的框架都需要与 Spring 整合, Spring 也号称框架中的框架, 其拓展性就不用我说了, 直接看源码 (本章节采用的是 注解, 并非 xml 配置文件)

搭建环境

@Configuration
@ComponentScan("com.gp.ibatis")
@MapperScan("com.gp.ibatis.mapper")
public class MapperScanDemo {

    private final Resource configResource = new ClassPathResource("conf.xml");

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setConfigLocation(configResource);
        return sqlSessionFactoryBean;
    }


    @Bean
    public DataSource dataSource(){
        ....
    }
}

只需要添加一个注解, 并手动注册一个 Bean : SqlSessionFactoryBean, mybaits 就能在 Spring 中发挥左右了, 我们先来看看这个 @MapperScan 注解

@MapperScan

mybatis 自定义了三个重要的类 : MapperScannerConfigurer, MapperScannerRegistrar, ClassPathMapperScanner

MapperScannerRegistrar

@MapperScan 是一个复合注解, 里面包含一个 @Import(MapperScanRegistrar.class), 手动向 spring 中注册了一个对象 (并没有立即放入 BeanFactory 中, 先将其存放在一个 Map 中, 后面会分析)

该类何时干活的?

在准备完 BeanFactory (默认由 DefaultListableBeanFactory 实现) 后, 在 refresh() 中执行 invokeBeanFactoryPostProcessor() , 继续点进入一个静态方法, 在 PostProcessorRegistrationDelegate 的 invokeBeanFactoryPostProcessors 方法中有这样一段代码 :

String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    // 检查该 postProcessorNames 对应的类是否实现了 PriorityOrdered 接口
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
      //如果执行完 beanFactory.getBean(), 该 bean 就会实例化并放入 单例池 中
      //这里会拿到 spring 内部定义的类 : ConfigurationClassPostProcessor
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
// ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口
// 执行其 postProcessBeanDefinitionRegistry, 完成扫描
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
currentRegistryProcessors.clear();

在 spring 在扫描的过程中, 会递归处理 @Import 注解, 详看源码 :

// 位于 ConfigurationClassParser#processImports() , 只提取一段
if (candidate.isAssignable(ImportSelector.class)) {
    Class<?> candidateClass = candidate.loadClass();
    // 实例化 ImportSelector 对象
    ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
    ParserStrategyUtils.invokeAwareMethods(
        selector, this.environment, this.resourceLoader, this.registry);
    if (selector instanceof DeferredImportSelector) {
        this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
    }
    else {
        String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
        Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
       // 递归处理
        processImports(configClass, currentSourceClass, importSourceClasses, false);
    }
}
// 这里就是处理 ImportBeanDefinitionRegistrar 的
// MapperScannerRegistrar 实现了该接口
else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
    Class<?> candidateClass = candidate.loadClass();
    ImportBeanDefinitionRegistrar registrar =
        BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
    ParserStrategyUtils.invokeAwareMethods(
        registrar, this.environment, this.resourceLoader, this.registry);
    // 这里就会把 MapperScannerRegistrar 对象放入一个 Map 中
    configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
}
else {
    this.importStack.registerImport(
        currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
    // 将 @Import 的普通类视为 ConfigurationClass 继续进行处理
    processConfigurationClass(candidate.asConfigClass(configClass));
}

从 spring 源码中可以看出, @Import 只能导入三种类型 : 1. ImportSelector 2. ImportBeanDefinitionRegistrar 3. 其他普通类 (类似 @Component 类)

可以实现 ImportSelector , ImportBeanDefinitionRegistrar 接口对 spring 进行拓展

那么何时处理缓存 ImportBeanDefinitionRegistrar 类型的集合呢?

在 spring 完成扫描后, 会遍历所有 configClass (包括 @Import 导入的类, @Component 的类都会被 spring 放入一个集合中, 视为 configClass )

在处理标注了 @MapperScan 的 configClass 时, 会依次处理 @Import 的类, @Bean 方法, 最后处理所有的 ImportBeanDefinitionRegistrar , MapperScannerRegistrar 到此时才开始工作

// 处理被 @Import 导入的类, 将其包装成 BeanDefinition 注册进 BeanFactory
if (configClass.isImported()) {
    registerBeanDefinitionForImportedConfigurationClass(configClass);
}
// 处理所有的 @Bean 方法, 将 @Bean 方法转换成 BeanDefinition 注册进 BeanFactory
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    loadBeanDefinitionsForBeanMethod(beanMethod);
}

// 处理 @ImportResource 注解
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 处理 ImportBeanDefinitionRegistrar, 执行他们的 registerBeanDefinitions() 方法
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());

点击 MapperScannerRegistrar 看会发现, 先是拿到元数据, 创建 beanClass 为 MapperScannerConfigurer 的 BeanDefinition 对象, 然后就是一堆的判断, 最后将 BeanDefinition 对象注册进 BeanFactory

MapperScannerConfigurer

该接口最重要的是实现了 BeanDefinitionRegistryPostProcessor 接口

还是在 PostProcessorRegistrationDelegate 的 invokeBeanFactoryPostProcessors 方法中, 执行完实现了 PriorityOrdered , Ordered 接口的 BeanDefinitionRegistryPostProcessor 后, 再来执行剩下的, 而此时 MapperScannerConfigurer 开始干活 (从这里可以看出 PriorityOrdered 的优先级高于 Ordered)

if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }
	// 此类继承自 ClassPathBeanDefinitionScanner, 用于扫描工作
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
	// 设置一些 mybatis 需要的参数
    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));
    }
	// 手动注册了一个 includeFilter, 始终返回 true
    scanner.registerFilters();
	// 扫描指定路径的所有接口
    scanner.scan(...);

ClassPathMapperScanner

该类继承自 ClassPathBeanDefinitionScanner, 并重写了 doScan 和 isCandidateComponent 方法

// 扫描的方式和 spring 类似
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 这里根据过滤器判断该 class 文件对应的类是否标注 @Conditional 注解
if (isCandidateComponent(metadataReader)) {
    ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
    sbd.setSource(resource);
    // 该方法被 ClassPathMapperScanner 重写了
    // 其实就是判断该类型是否为 接口且是独立开放的 (不为 private 或 default)
    if (isCandidateComponent(sbd)) {
        if (debugEnabled) {
            logger.debug("Identified candidate component class: " + resource);
        }
        // 如果两个判断通过, mybatis 会认为这是一个符合的 dao
        // 最后会被注册进 BeanFactory 
        candidates.add(sbd);
    }

@Mapper

如果没有标注 @MapperScan 注解, 直接在接口上标注 @Mapper , 也能从容器中获取到, 这是为什么呢?

这种方式就是 spring boot 的自动配置类起作用了, MybatisAutoConfiguration 有两个内部类 :

public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
    ....
}

@org.springframework.context.annotation.Configuration
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

MapperScannerRegistrarNotFoundConfiguration 作为一个配置类, 能被 spring boot 拿到, 当处理该类时, spring boot 就会判断容器中是否存在 MapperFactoryBean, MapperScannerConfigurer 这两种 bean 时, 如果不存在, 该配置类就会生效, Import 一个 AutoConfiguredMapperScannerRegistrar, 该类实现了 ImportBeanDefinitionRegistrar 接口, 在 spring 中同样的方式往容器中注册了 beanClass 为 MapperScanConfigurer 的 BeanDefinition 对象, 之后的流程就和上面一样了…

现在已经得到封装 Mapper 的 BeanDefinition 对象了, 还需要 mybatis 做一些工作, 修改此 Mapper 的 beanClass, 这个 Mapper 接口实际类是 MapperFactoryBean

还是 ClassPathMapperScanner 这个类, 在得到所有封装 Mapper 的 BeanDefinition 对象 (这个 BeanDefinition 被 BeanDefinitionHolder 封装了) 后, 调用他的 processBeanDefinitions 方法

GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
    definition = (GenericBeanDefinition) holder.getBeanDefinition();

    ....

   
    // 修改此 Mapper 接口的 beanClass, MapperFactoryBean
  
    
    definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
    definition.setBeanClass(this.mapperFactoryBean.getClass());

  
    ....
    // 最后将此 Mapper 的注入模型修改为 by type
    definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }
}

现在 Mapper 的 beanClass 已被修改

实例化 Mapper

spring 通过 JDK 提供的内省 : Introspector 得到 MapperFactoryBean 的 BeanInfo, 通过 BeanInfo 得到所有 PropertyDescriptor 对象 (这是一个数组), PropertyDescriptor 描述Java Bean 通过一对访问器方法导出的一个属性

利用这个 PropertyDescriptor 数组得到 sqlSessionFactory , 和一个 sqlSessionTemplate , 创建依赖关系, 但是只会把 SqlSessionFactory 充当 Mapper 的属性, 在实例化 Mapper 的时候, 就会为这个属性名为 sqlSessionFactory 注入值

由于封装 Mapper 的 BeanDefinition 的自动装配模型设置为 : byType, 我们查看 AbstractAutowireCapableBeanFactory 的 autowireByType 方法

protected void autowireByType(
    String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {

    ....

    Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
    // 得到属性 name
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        try {
            // 通过 JDK 的 Introspector 得到 MapperFactoryBean 的所有 PropertyDescriptor
            PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
            // Don't try autowiring by type for type Object: never makes sense,
            // even if it technically is a unsatisfied, non-simple property.
            if (Object.class != pd.getPropertyType()) {
                MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                // Do not allow eager init for type matching in case of a prioritized post-processor.
                boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
                // 创建依赖关系, 相当于被属性标注 @Autowire 注解
                DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);          
                // 处理依赖关系, 完成依赖注入 (实例化 SqlSessionFactory)
                Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                if (autowiredArgument != null) {
                    pvs.add(propertyName, autowiredArgument);
                }
                ....
}

在这个时候就会实例化 SqlSessionFactory, 这个是一个 FactoryBean : SqlSessionFactoryBean , 这个 bean 同时实现了 InitializingBean 接口, 所以在实例化之前, 会先进行初始化, 执行 加载mybatis全局配置文件, 初始化数据源 … 等工作

有了 SqlSessionFactory 对象, MapperFactoryBean 就能调用 setSqlSessionFactory(SqlSessionFactory) 方法设置 SqlSession 属性 (实现类为 SqlSessionTemplate )

MapperFactoryBean

每个 Dao 接口都是一个 MapperFactoryBean, 调用它的 getObject 得到对象

// 代码很短, 就是 mybatis 的原代码, 利用 Sqlsession 得到 Mapper 的代理对
public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
}

猜你喜欢

转载自blog.csdn.net/Gp_2512212842/article/details/107681385