浅析@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)
blank

相关推荐

  • DDL和DML的含义与区别「建议收藏」

    1、DDL和DML的含义1、DML(DataManipulationLanguage)数据操作语言-数据库的基本操作,SQL中处理数据等操作统称为数据操纵语言,简而言之就是实现了基本的“增删改查”操作。包括的关键字有:select、update、delete、insert、merge2、DDL(DataDefinitionLanguage)数据定义语言-用于定义和管理SQL数据库中的所有对象的语言,对数据库中的某些对象(例如,database,table)进行管理。包括的关键字有:crea

  • idea安装mybatis插件_myeclipse安装插件

    idea安装mybatis插件_myeclipse安装插件选择Help->Installfromsite…在弹出的对话框中点击右上角的Add按钮Name:mybatisLocation:https://dl.bintray.com/mybatis/mybatis-generator

  • springboot线程池的使用和扩展「建议收藏」

    springboot线程池的使用和扩展「建议收藏」我们常用ThreadPoolExecutor提供的线程池服务,springboot框架提供了@Async注解,帮助我们更方便的将业务逻辑提交到线程池中异步执行,今天我们就来实战体验这个线程池服务;本文地址:http://blog.csdn.net/boling_cavalry/article/details/79120268实战环境windowns10;jdk1.8;spring

  • ODT下载安装步骤「建议收藏」

    ODT下载安装步骤「建议收藏」ODT下载安装步骤

  • 灰度共生矩阵的实现条件_灰度共生矩阵14个特征

    灰度共生矩阵的实现条件_灰度共生矩阵14个特征由于纹理是由灰度分布在空间位置上反复出现而形成的,因而在图像空间中相隔某距离的两象素之间会存在一定的灰度关系,即图像中灰度的空间相关特性。灰度共生矩阵就是一种通过研究灰度的空间相关特性来描述纹理的常用方法。

  • btav狼php,www.bvlang5.com[通俗易懂]

    btav狼php,www.bvlang5.com[通俗易懂]DomainName:BVLANG5.COMRegistryDomainID:2183015930_DOMAIN_COM-VRSNRegistrarWHOISServer:whois.namesilo.comRegistrarURL:http://www.namesilo.comUpdatedDate:2017-11-04T09:00:14ZCreationDate:2…

发表回复

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

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