mybatis-spring整合源码解析

Mybatis对Spring的整合实现

本文只讨论整合Spring,Mybatis是如何整合到Spring生态中的

接口扫描的MapperScan的实现和扩展

@MapperScan, 元标注了@Import注解,导入了一个MapperScannerRegistrarConfiguration Class , 申明如下

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware

我们都知道ImportBeanDefinitionRegistrar 是Spring注入Configuration Class到容器中的一种常见手段, 常见的还有@ImportImportSelector.. , 该实现类的核心逻辑在registerBeanDefinitions()中,如下

 //importingClassMetadata 为当前标注了@Import的Configuration Class的注解元信息
//BeanDefinitionRegistry registry 为当前BeanFacatory的引用
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
   //1. 获取@MapperScan注解的属性信息
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
     // 2. 注册一个名为 MapperScannerConfigurer的 Bean到IOC容器中
      registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
    }
  }
    
  // 具体注册Bean的逻辑
  void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
        //1. 构造BeanDefinition
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    builder.addPropertyValue("processPropertyPlaceHolders", true);
        //2. 设置自定义的注解,在后面自定义扫描有大用处
    Class annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      builder.addPropertyValue("annotationClass", annotationClass);
    }
        //3. 设置自定义的接口 Class,在后面自定义扫描有大用处
    Class markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      builder.addPropertyValue("markerInterface", markerInterface);
    }
    
    //....省略部分PropertyValues的属性赋值

MapperScannerConfigurer的用处以及实现原理

是一个BeanDefinitionRegistryPostProcessor的实现类,该类型会在Spring容器启动刷新时进行回调

查看源码发现其类的声明如下

//1. 发现其是一个BeanDefinitionRegistryPostProcessor , 该类型接口会在IOC容器刷新的时候进行回调
public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware
  
//2. 能在回调方法中发现其核心做了两件事情
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders(); //解析相关包名占位符
    }
        //2.1 创建自定义的ClassPathBeanDefinitionScanner(Spring中@ComponentScan核心处理类)并添加自定义的扫描类型
    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();
   //2.2 进行扫描获取BeanDefinition,并注册到容器中
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

接下来我们来看ClassPathMapperScanner组件的用处 , 他其实是扩展了spring的@ComponentScan的组件扫描方式,核心看registerFilters()方法,里面添加了要扫描的TypeFilter的方式

  public void registerFilters() {
    boolean acceptAllInterfaces = true; //1. 这个标志位是否要扫描包下所有的接口
        
        //2. 这里的annotationClass是前面注册MapperScannerConfigurer时传递进来的自定义注解属性
    if (this.annotationClass != null) {
        // 2.1 这里添加IncludeFilter表示,要添加一个允许的扫描注解,只要标注了该注解就会被ClassLoader扫描到
      addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
      acceptAllInterfaces = false;
    }

        //3. 这里的annotationClass是前面注册MapperScannerConfigurer时传递进来的自定义接口Class
    if (this.markerInterface != null) {
      //3.1 扫描自定义的接口类型,并且
      addIncludeFilter(new AssignableTypeFilter(this.markerInterface) {
        @Override
        protected boolean matchClassName(String className) {
          //不能实现类Class,只能是抽象接口或者抽象类
          return false;
        }
      });
      acceptAllInterfaces = false;
    }
        //4. 如果没有自定义注解或者自定义接口扫描,那么添加一个TypeFilter默认全部扫描所有
    if (acceptAllInterfaces) {
      // default include filter that accepts all classes
      addIncludeFilter((metadataReader, metadataReaderFactory) -> true);
    }

    // exclude package-info.java
    addExcludeFilter((metadataReader, metadataReaderFactory) -> {
      String className = metadataReader.getClassMetadata().getClassName();
      return className.endsWith("package-info");
    });
  }

扫描时如何根据IncludeFilter/ExcludeFilter进行扫描和过滤?核心方法调用链如下

//调用链
//ClassPathMapperScanner#doScan() -> ClassPathBeanDefinitionScanner#doScan() -> ClassPathScanningCandidateComponentProvider#findCandidateComponents()  -> scanCandidateComponents()
 
 //其中scanCandidateComponents()方法具体实现如下
  private Set scanCandidateComponents(String basePackage) {
        Set candidates = new LinkedHashSet<>();
        try {
            //1. 获取传递进来的扫描包路径
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      //2. 使用ResourceLoader加载资源
            Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
            boolean traceEnabled = logger.isTraceEnabled();
            boolean debugEnabled = logger.isDebugEnabled();
            for (Resource resource : resources) {
                if (traceEnabled) {
                    logger.trace("Scanning " + resource);
                }
                if (resource.isReadable()) {
                    try {
           //3. 使用ASM进行元信息读取
                        MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
            //4. 这里很关键里面会进行IncludeFilter和ExcludeFilter的判断,也是能自定义扩展组件扫描的核心方法
                        if (isCandidateComponent(metadataReader)) {
            // 5. 拼装成BeanDefinition,后面会给BeanDefinition设置beanClass为MapperFactoryBean代理对象
             //6. 最后注册到IOC容器中,此时我们已经可以使用Mybatis的Mapper来完成依赖注入和依赖查找了
                            ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                            sbd.setSource(resource);
                            if (isCandidateComponent(sbd)) {
                                if (debugEnabled) {
                                    logger.debug("Identified candidate component class: " + resource);
                                }
                                candidates.add(sbd);
                            }
    //省略部分无关源码...

其中isCandidateComponent()实现如下

    protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
        for (TypeFilter tf : this.excludeFilters) { //遍历所有的ExcludeFilter,若有匹配的则返回false不进行扫描
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return false;
            }
        }
            //遍历所有的IncludeFilter,若匹配则进行Conditional条件注解判断,这里includeFilters中就包括了之前
            //ClassPathMapperScanner#registerFilters()方法中注册的includeFilters。这也是为什么我们配置了
            // @MapperScan(basePakages="xxxx")就能扫描到xxx包下的所有类到ioc容器中的所有原理
        for (TypeFilter tf : this.includeFilters) { 
            if (tf.match(metadataReader, getMetadataReaderFactory())) {
                return isConditionMatch(metadataReader);
            }
        }
        return false;
    }

你可能感兴趣的:(mybatis-spring整合源码解析)