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

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

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

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

代码语言:javascript复制@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 nameGenerator() default BeanNameGenerator.class;

// 指定自定义注解进行扫描,,通常默认

Class annotationClass() default Annotation.class;

// 指定自定义接口进行扫描,通常默认

Class markerInterface() default Class.class;

// 指定sqlSessionTemplate,通常默认

String sqlSessionTemplateRef() default "";

// 指定sqlSessionFactory,通常默认

String sqlSessionFactoryRef() default "";

// MapperFactoryBean类型,通常默认

Class factoryBean() default MapperFactoryBean.class;

// 是否懒加载bean,通常默认

String lazyInitialization() default "";

}2. MapperScannerRegistrar@MapperScan上面注解@Import(MapperScannerRegistrar.class),说明具体逻辑在MapperScannerRegistrar里面。

@Import有三种用法,其中一种是实现ImportBeanDefinitionRegistrar接口。引入时,会调用registerBeanDefinitions方法。

代码语言:javascript复制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 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 generatorClass = annoAttrs.getClass("nameGenerator");

if (!BeanNameGenerator.class.equals(generatorClass)) {

builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));

}

Class 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 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. MapperScannerConfigurerMapperScannerConfigurer主要实现了BeanDefinitionRegistryPostProcessor接口。

BeanDefinitionRegistryPostProcessor会在spring启动时调用postProcessBeanDefinitionRegistry方法。

结构如下:

代码语言:javascript复制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方法

代码语言:javascript复制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 doScan(String... basePackages) {

Assert.notEmpty(basePackages, "At least one base package must be specified");

Set beanDefinitions = new LinkedHashSet<>();

for (String basePackage : basePackages) {

// 扫描路径获取路径下的BeanDefinition

Set 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。

代码语言:javascript复制public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {

@Override

public Set doScan(String... basePackages) {

// 调用父类的doScan,将路径转为beanDefinition,然后再封装为BeanDefinitionHolder

Set 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 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;

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

相关推荐

快手手机号码价格探讨与购买建议
365betappios

快手手机号码价格探讨与购买建议

📅 08-01 👁️ 3642
安全验证
365betappios

安全验证

📅 09-19 👁️ 3498
腿和什么一起练
be365

腿和什么一起练

📅 10-28 👁️ 7395
米 到 厘米 转换器
beat365英超欧冠平台

米 到 厘米 转换器

📅 07-08 👁️ 4226
如何提高1000米跑步技巧(让你在短时间内提高成绩的15个技巧)
便携式DVD十大品牌排行榜
be365

便携式DVD十大品牌排行榜

📅 07-13 👁️ 4057