美文网首页
mybatis-spring整合源码解析

mybatis-spring整合源码解析

作者: just_like_you | 来源:发表于2020-11-12 22:04 被阅读0次

    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<? extends Annotation> 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<BeanDefinition> scanCandidateComponents(String basePackage) {
            Set<BeanDefinition> 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整合源码解析

          本文链接:https://www.haomeiwen.com/subject/lmrmbktx.html