浅析@MapperScan原理[通俗易懂]

浅析@MapperScan原理[通俗易懂]@MapperScan是spring用于批量注入mybatis映射器(DAO接口)的注解。与之相对应@Mapper进行单个注册。源码如下:@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Documented@Import(MapperScannerRegistrar.class)@Repeatable(MapperScans.class)public@interfaceMapperScan{//指定

大家好,又见面了,我是你们的朋友全栈君。

1. @MapperScan属性

@MapperScan 是spring用于批量注入mybatis映射器(DAO接口)的注解。与之相对应@Mapper进行单个注册。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan { 
   
  
  // 指定扫描包路径信息,等效于basePackages设置
  // 使用该字段是便于@MapperScan简写,例如
  // @MapperScan("com.demo.dao")等效@MapperScan(basePackages="com.demo.dao")
  String[] value() default { 
   };
  // 指定扫描包路径信息,多个可以,或者;进行分割
  String[] basePackages() default { 
   };
  // 指定扫描的类路径
  Class<?>[] basePackageClasses() default { 
   };
  // 扫描注册的bean命名规则,通常默认 
  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
  // 指定自定义注解进行扫描,,通常默认 
  Class<? extends Annotation> annotationClass() default Annotation.class;
  // 指定自定义接口进行扫描,通常默认 
  Class<?> markerInterface() default Class.class;
  // 指定sqlSessionTemplate,通常默认 
  String sqlSessionTemplateRef() default "";
  // 指定sqlSessionFactory,通常默认 
  String sqlSessionFactoryRef() default "";
  // MapperFactoryBean类型,通常默认 
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
  // 是否懒加载bean,通常默认 
  String lazyInitialization() default "";

}

2. MapperScannerRegistrar

@MapperScan上面注解@Import(MapperScannerRegistrar.class),说明具体逻辑在MapperScannerRegistrar里面。
@Import有三种用法,其中一种是实现ImportBeanDefinitionRegistrar接口。引入时,会调用registerBeanDefinitions方法。

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware { 
   

  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 
   
  	// 获取MapperScan注解的属性属性
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) { 
   
      registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
          generateBaseBeanName(importingClassMetadata, 0));
    }
  }

  void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
      BeanDefinitionRegistry registry, String beanName) { 
   
	// 根据注解信息,注册一个MapperScannerConfigurer
    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"));
    }
	// 待扫描包路径集合
    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()));

    if (basePackages.isEmpty()) { 
   
      basePackages.add(getDefaultBasePackage(annoMeta));
    }

    String lazyInitialization = annoAttrs.getString("lazyInitialization");
    if (StringUtils.hasText(lazyInitialization)) { 
   
      builder.addPropertyValue("lazyInitialization", lazyInitialization);
    }

    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));

    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }
}

3. MapperScannerConfigurer

MapperScannerConfigurer主要实现了BeanDefinitionRegistryPostProcessor接口。
BeanDefinitionRegistryPostProcessor会在spring启动时调用postProcessBeanDefinitionRegistry方法。
结构如下:
在这里插入图片描述

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { 
   
    
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { 
   
		// 这里进行占位符相关操作
	    if (this.processPropertyPlaceHolders) { 
   
	      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(lazyInitialization)) { 
   
	      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
	    }
	    scanner.registerFilters();
	    // 这里是进行实践的扫描注册操作
	    // StringUtils.tokenizeToStringArray是分给数组,匹配,或者;
	    scanner.scan(
	        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
	  }
}

ClassPathMapperScanner 不存在scan,这里调用的是父类scan方法

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider { 
   
	
	public int scan(String... basePackages) { 
   
		int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
		// ClassPathMapperScanner重写doScan
		doScan(basePackages);

		// Register annotation config processors, if necessary.
		if (this.includeAnnotationConfig) { 
   
			AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
		}

		return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
	}
	
	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)) { 
   
					// 将BeanDefinition进一步封装为BeanDefinitionHolder 
					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
					definitionHolder =
							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
					beanDefinitions.add(definitionHolder);
					registerBeanDefinition(definitionHolder, this.registry);
				}
			}
		}
		return beanDefinitions;
	}
}

ClassPathMapperScanner 重写了doScan方法,主要是扫描路径,并将扫描的信息转为beanDefinition,设置其为MapperFactoryBean。

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { 
   
  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) { 
   
  	// 调用父类的doScan,将路径转为beanDefinition,然后再封装为BeanDefinitionHolder
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) { 
   
      LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
          + "' package. Please check your configuration.");
    } else { 
   
      // 这里对beanDefinition进一步加工
      processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
  }
}

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { 
   
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) { 
   
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
          + "' mapperInterface");

      // 重点,MapperFactoryBean 设置构造参数,这里为dao接口的全限定名
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); 
      // 重点,这里设置为MapperFactoryBean
      definition.setBeanClass(this.mapperFactoryBeanClass);
	  // 以下常规设置可忽略
      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      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(
              () -> "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.");
        }
        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);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }

4. 总结

@MapperScan 实际做的事情:
1.扫描指定路径,并将路径下的信息记录为BeanDefinition;
2.将获取的BeanDefinition,设置为MapperFactoryBean,注入IOC;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/140890.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 简述php的垃圾收集机制

    简述php的垃圾收集机制

  • mysql的float取值范围_mysql float精度与范围总结 – numeric「建议收藏」

    mysql的float取值范围_mysql float精度与范围总结 – numeric「建议收藏」…是ture,不适用范围:不适用于检测可为0的参数。is_numeric();——只适用于检测数字,但假如参数名不存在,会出错,因此不适合于第一层检测。综合示例:复制代码代码如下:FLOAT类型用于表示近似数值数据类型。SQL标准允许在关键字FLOAT后面的括号内选择用位指定精度(但不能为指数范围)。MySQL还支持可选的只用于确定存储大小的精度规定。0到23的精度对应FLOAT列的4…

  • 碟刹和V刹的区别「建议收藏」

    碟刹和V刹的区别「建议收藏」0首先拍死的一个观点就是碟刹比V刹要好,要高档──似乎大部分对于运动自行车陌生新手往往认为碟刹一定比V刹要好,我们听到过这样的话:“都2000多的车了,还没有碟刹”───这样的话真的让人哭笑不得,看看不论是国外的比赛还是国内的专业比赛,如果是晴天的比赛,V刹车还是占了大部分的,当然目前也有碟刹车增多的趋势,但是对于大部分休闲骑行和不参加业余级别比赛的车友的来说,V刹尤其是好些的V刹还是够用的,…

  • python 菜鸟教程 正则_华为mate30好用不

    python 菜鸟教程 正则_华为mate30好用不正则表达式简介正则表达式,是一个特殊的字符序列,又称规则表达式(英语:RegularExpression,在代码中常简写为regex、regexp或RE),本质而言是一种小型的,高度专业化的编程语言。Python自1.5版本起增加了re模块,re模块使Python语言拥有全部的正则表达式功能。正则语法表关于正则语法表,别想其他的都背过就行了。不管你是python还是其他…

  • 联想笔记本键盘亮了屏幕不亮怎么办_电脑开机显示器和键盘都不亮

    联想笔记本键盘亮了屏幕不亮怎么办_电脑开机显示器和键盘都不亮联想电脑显示器不亮怎么办联想电脑显示器不亮解决方法一:1、开机后,我们先不管显示器是否能正常的亮或显示,我们先再次按主机上的重启键,然后我们按一下键中的“numlock”键,也就是台式键盘右边的数字开关切换键。2、如数字开关键上面的数字锁定灯可以正常的亮或正常的灭,这时就说明电脑主机一般没啥事儿了,基本上可以确定是由显示器本身的问题了。3、如无法显示正常的灯亮和灯灭的话,那么基本可以说明是电脑机…

  • 【4】进大厂必须掌握的面试题-Java面试-jdbc

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 1.什么是JDBC驱动程序? JDBC驱动程序是使Java应用程序与数据库进行交互的软件组件。JDBC驱动程序有4种…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号