【Mybatis】【二】源码分析-Mapper 接口都是怎么注入到 Spring容器中的?

1  前言

续上节主要用调试的方式,看了一下 Mapper 接口的注入过程,本节就从源码的角度,一步步分析 Mapper 接口都是怎么注入到 Spring容器中的。

2  源码分析

2.1  入口分析

我们上节看到扫描 @Mapper 的方式有两种,一种是基于 @MapperScan 的主动扫描,另一种就是基于默认包扫描 @Mapper 的方式,但两种方式最后都是落在 MapperScannerConfigurer 这个类来处理。两种方式还是有一点不一样的,我们从源码看下。

2.1.1  主动方式(@MapperScan)

我们首先来看下主动方式的:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
// 可以看到这里引入了 MapperScannerRegistrar
@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.class)
public @interface MapperScan {
    String[] value() default {};
}

关于 @MapperScan 的默认定义信息:

@Import({MapperScannerRegistrar.class})
@Repeatable(MapperScans.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 "";
    // 默认的 FactoryBean
    Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

    String lazyInitialization() default "";

    String defaultScope() default "";
}

我们继续跟进去看 MapperScannerRegistrar:

// 可以看到 MapperScannerRegistrar 实现了 ImportBeanDefinitionRegistrar
// 也就是说它是 BeanDefinition 的一个注册器
// registerBeanDefinitions 方法来注入一些 BeanDefinition
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    public MapperScannerRegistrar() {
    }

    /** @deprecated */
    @Deprecated
    public void setResourceLoader(ResourceLoader resourceLoader) {
    }
    // 扫描注入
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        if (mapperScanAttrs != null) {
            this.registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
        }
    }
}

我们继续看看内部的 registerBeanDefinitions 方法:

void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    builder.addPropertyValue("processPropertyPlaceHolders", true);
    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);
    }
    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
        builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
    }
    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
        builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
    }
    String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
        builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
    }
    String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
    if (StringUtils.hasText(sqlSessionFactoryRef)) {
        builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
    }
    // 这里就是获取我们 @MapperScan 上提供的包名
    List<String> basePackages = new ArrayList();
    basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
    basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
    basePackages.addAll((Collection)Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
    if (basePackages.isEmpty()) {
        basePackages.add(getDefaultBasePackage(annoMeta));
    }
    String lazyInitialization = annoAttrs.getString("lazyInitialization");
    if (StringUtils.hasText(lazyInitialization)) {
        builder.addPropertyValue("lazyInitialization", lazyInitialization);
    }
    String defaultScope = annoAttrs.getString("defaultScope");
    if (!"".equals(defaultScope)) {
        builder.addPropertyValue("defaultScope", defaultScope);
    }
    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}

可以看到注入了一个 MapperScannerConfigurer 类型的 BeanDefinition。MapperScannerConfigurer 又是一个 Bean 定义信息的后置处理器,来注入一些 BeanDefinition。

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    private String basePackage;
    private boolean addToConfig = true;
    ...
}

那我们看它的 postProcessBeanDefinitionRegistry 方法:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
        this.processPropertyPlaceHolders();
    }
    // 构造 ClassPathMapperScanner 来进行扫描
    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(this.lazyInitialization)) {
        scanner.setLazyInitialization(Boolean.valueOf(this.lazyInitialization));
    }
    if (StringUtils.hasText(this.defaultScope)) {
        scanner.setDefaultScope(this.defaultScope);
    }
    // 注入扫描的过滤器
    scanner.registerFilters();
    // 扫描 @MapperScan 提供的包名
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}

我们看一下它注入的过滤器,看看它的筛选规则:

public void registerFilters() {
    boolean acceptAllInterfaces = true;
    // @MapperScan 方式引入的,this.annotationClass 这里是空
    if (this.annotationClass != null) {
        this.addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
        acceptAllInterfaces = false;
    }
    // 这个也是空
    if (this.markerInterface != null) {
        this.addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
            protected boolean matchClassName(String className) {
                return false;
            }
        });
        acceptAllInterfaces = false;
    }
    // true
    if (acceptAllInterfaces) {
        // 白名单直接全部返回 true
        this.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
            return true;
        });
    }
    // 黑名单  过滤掉不是 package-info 结尾的
    this.addExcludeFilter((metadataReader, metadataReaderFactory) -> {
        String className = metadataReader.getClassMetadata().getClassName();
        return className.endsWith("package-info");
    });
}

从过滤器的规则看下来,@MapperScan 方式引入的会扫描注入包下的除了 package-info 外的所有类。

ClassPathMapperScanner 本身又是继承 Spring 的 ClassPathBeanDefinitionScanner :

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {...}

我们继续跟进去,看看 ClassPathMapperScanner 的扫描方法:

public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
    // 开始进行扫描
    doScan(basePackages);
    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }
    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

doScan 就开始进行扫描注入了,我们跟进去:

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 直接调用父类的包扫描
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
        LOGGER.warn(() -> {
            return "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.";
        });
    } else {
        this.processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
}
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   // 多个包的话就逐个进行扫描
    for (String basePackage : basePackages) {
        // 筛选包下符合条件的 BeanDefinition 
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
            candidate.setScope(scopeMetadata.getScopeName());
            String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
            if (candidate instanceof AbstractBeanDefinition) {
                postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
            }
            if (candidate instanceof AnnotatedBeanDefinition) {
                AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
            }
            if (checkCandidate(beanName, candidate)) {
                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                definitionHolder =
                        AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
                beanDefinitions.add(definitionHolder);
                registerBeanDefinition(definitionHolder, this.registry);
            }
        }
    }
    return beanDefinitions;
}

注意这里的 findCandidateComponents 筛选,就会根据刚才的过滤器规则,先进行黑名单校验(主要不是package-info的类),再进行白名单(默认都是true)的校验,然后得出符合条件的 BeanDefinition.

得到符合条件的 BeanDefinition,这时候对 BeanDefinition 进行修改,改变为 MapperFactoryBean 的方式。

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    BeanDefinitionRegistry registry = this.getRegistry();
    Iterator var4 = beanDefinitions.iterator();
    while(var4.hasNext()) {
        BeanDefinitionHolder holder = (BeanDefinitionHolder)var4.next();
        AbstractBeanDefinition definition = (AbstractBeanDefinition)holder.getBeanDefinition();
        boolean scopedProxy = false;
        if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
            definition = (AbstractBeanDefinition)Optional.ofNullable(((RootBeanDefinition)definition).getDecoratedDefinition()).map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> {
                return new IllegalStateException("The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]");
            });
            scopedProxy = true;
        }
        String beanClassName = definition.getBeanClassName();
        LOGGER.debug(() -> {
            return "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName + "' mapperInterface";
        });
        definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
        definition.setBeanClass(this.mapperFactoryBeanClass);
        definition.getPropertyValues().add("addToConfig", this.addToConfig);
        definition.setAttribute("factoryBeanObjectType", beanClassName);
        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;
        }
        if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
            if (explicitFactoryUsed) {
                LOGGER.warn(() -> {
                    return "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(() -> {
                    return "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.";
                });
            }
            definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
            explicitFactoryUsed = true;
        }
        if (!explicitFactoryUsed) {
            LOGGER.debug(() -> {
                return "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.";
            });
            definition.setAutowireMode(2);
        }
        definition.setLazyInit(this.lazyInitialization);
        if (!scopedProxy) {
            if ("singleton".equals(definition.getScope()) && this.defaultScope != null) {
                definition.setScope(this.defaultScope);
            }
            if (!definition.isSingleton()) {
                BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
                if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
                    registry.removeBeanDefinition(proxyHolder.getBeanName());
                }
                registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
            }
        }
    }
}

是不是有点乱,我们画个图捋一下:

2.1.2  被动默认方式

看完主动扫描方式的,我们继续看第二种方式,也就是默认的被动方式,它的入口是在 Mybatis 的自动装配的类MybatisAutoConfiguration 里:

@Configuration
// 引入
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
// 条件机制 在没有 MapperScannerConfigurer 的情况下,也就是没有@MapperScan的情况下
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    public MapperScannerRegistrarNotFoundConfiguration() {
    }
    public void afterPropertiesSet() {
        MybatisAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }
}

那我们继续看引入的AutoConfiguredMapperScannerRegistrar:

// Bean 定义的注册器
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar {
    private BeanFactory beanFactory;
    public AutoConfiguredMapperScannerRegistrar() {
    }
    // 注册 BeanDefinition
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        if (!AutoConfigurationPackages.has(this.beanFactory)) {
            MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
        } else {
            MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
            List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
            if (MybatisAutoConfiguration.logger.isDebugEnabled()) {
                packages.forEach((pkg) -> {
                    MybatisAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);
                });
            }
            // 可以看到注入的类型就是 MapperScannerConfigurer
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
            builder.addPropertyValue("processPropertyPlaceHolders", true);
            // 看这里就是跟上边的 MapperScannerConfigurer 不一样的地方,这里设置属性,会影响到注册过滤器的地方 会根据 @Mapper 注解进行过滤了,而不是像上边扫描包下的除了 package-info外的所有接口了,这就是两者的一个区别哈
            builder.addPropertyValue("annotationClass", Mapper.class);
            // 扫描默认包 默认情况下是启动类所在的包
            builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
            BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
            Set<String> propertyNames = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());
            if (propertyNames.contains("lazyInitialization")) {
                builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
            }
            if (propertyNames.contains("defaultScope")) {
                builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
            }
            // 注入
            registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
        }
    }
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }
}

看我们调试一下:

源码我们就不一一看了哈,执行过程差不多,我这里就再把上边的图改一下:

2.1.3  方式差异

看完两种方式,可以看到主要有两个地方不一样,

(1)ClassPathMapperScan 的 mapperFactoryBean 来源不一样,第一种方式来源于注解,第二种方式来源于自身默认的,第二种默认的扫描也会设置 MapperScannerConfigurer 的 mapperFactoryBeanClass 但它为空,这时 ClassPathMapperScan 方法设置的时候会判断,设置的是空,就会塞默认的 MapperFactoryBean 哈:

public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
    this.mapperFactoryBeanClass = mapperFactoryBeanClass == null ? MapperFactoryBean.class : mapperFactoryBeanClass;
}

(2)过滤机制不一样,不一样的源点在 annotationClass 哈,第一种方式 annotationClass 为空,就扫描指定包下的所有不是 package-info 的接口,第二种方式指定了 annotationClass = Mapper.class 所以过滤只有 @Mapper 注解的:

public void registerFilters() {
    boolean acceptAllInterfaces = true;
    if (this.annotationClass != null) {
        this.addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
        acceptAllInterfaces = false;
    }
    if (this.markerInterface != null) {
        this.addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
            protected boolean matchClassName(String className) {
                return false;
            }
        });
        acceptAllInterfaces = false;
    }
    if (acceptAllInterfaces) {
        this.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
            return true;
        });
    }
    this.addExcludeFilter((metadataReader, metadataReaderFactory) -> {
        String className = metadataReader.getClassMetadata().getClassName();
        return className.endsWith("package-info");
    });
}

3  小结

好啦,本节有点长了,防止你们看困了,我把 MapperFactoryBean 生成的过程放到下一节了哈,我们下节见,本文有理解不对的地方欢迎指正哈。

posted @ 2024-03-01 06:51  酷酷-  阅读(230)  评论(0编辑  收藏  举报