1.ClassPathMapperScanner分析.md
2025/8/4大约 5 分钟
1.ClassPathMapperScanner分析.md
为什么要看他?
来源于程序启动的一段日志,看起来是mybatis扫描mapper的时候,扫描了两遍。
因此好奇,为什么会扫描两边,这不是影响启动效率吗
日志如下:
24-09-26 11:03:47.773 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'messageMapper' and 'com.lenovo.chinaservice.messagestore.infra.MessageMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:47.774 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : No MyBatis mapper was found in '[com.lenovo.chinaservice.messagestore.infra]' package. Please check your configuration.
2024-09-26 11:03:48.507 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'attachmentHistoryMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.AttachmentHistoryMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.508 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'attachmentMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.AttachmentMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.508 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'caseIrMappingMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.CaseIrMappingMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.509 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'caseServiceOrderMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.CaseServiceOrderMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.509 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'changeRecordCheckMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.ChangeRecordCheckMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.509 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'changeRecordHistoryMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.ChangeRecordHistoryMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.510 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'changeRecordMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.ChangeRecordMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.510 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'changeRecordSnapshotMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.ChangeRecordSnapshotMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.510 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'changeSolutionMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.ChangeSolutionMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.510 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'communicationRecordMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.CommunicationRecordMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.511 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'communicationRecordReplyMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.CommunicationRecordReplyMapper' mapperInterface. Bean already defined with the same name!
2024-09-26 11:03:48.511 WARN [fieldservicej,,] 20832 --- [ main] o.m.s.mapper.ClassPathMapperScanner : Skipping MapperFactoryBean with name 'constraintsMapper' and 'com.lenovo.fieldservicebath.document.repository.impl.mapper.ConstraintsMapper' mapperInterface. Bean already defined with the same name!
源码探究
文档描述
/**
* A {@link ClassPathBeanDefinitionScanner} that registers Mappers by {@code basePackage}, {@code annotationClass}, or
* {@code markerInterface}. If an {@code annotationClass} and/or {@code markerInterface} is specified, only the
* specified types will be searched (searching for all interfaces will be disabled).
* <p>
* This functionality was previously a private class of {@link MapperScannerConfigurer}, but was broken out in version
* 1.2.0.
*/
文档意思是 这是一个ClassPathBeanDefinitionScanner的实现类,用于扫描mapper,可以通过basePackage,annotationClass,markerInterface来指定扫描的范围。
如果指定了annotationClass和markerInterface,那么只会扫描指定的类型,不会扫描所有的接口。
所以可以理解,这个类就是用于扫描mapper的。但是有一个问题,为什么会扫描两遍呢?
扫描逻辑分析
1. 调用父类的扫描逻辑,将所有候选的bean对象 选择到
/**
* Calls the parent search that will search and register all the candidates. Then the registered objects are post
* processed to set them as MapperFactoryBeans
*/
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用父类的扫描功能,将所有的候选的bean对象扫描出来
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 {
// 扫描的bean定义不为空,自己进行处理
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
2. 处理扫描到的bean对象
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
// 1. 遍历所有的bean定义
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName+ "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
// 将实际类保存下来
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
// 2.将扫描的bean 类设置为 mybatis的mapperFactoryBean
definition.setBeanClass(this.mapperFactoryBeanClass);
// 3. 添加属性
definition.getPropertyValues().add("addToConfig", this.addToConfig);
// 4. 显式的声明调用工厂bean
boolean explicitFactoryUsed = false;
// 5. 添加sqlSessionFactory 如果sqlSessionFactoryBeanName不为空就将他引入,
// 如果sqlSessionFactory不为空就将他引入
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
// 将 sqlSessionFactoryBeanName 作为属性添加到bean定义中
// RuntimeBeanReference 是一个运行时的bean引用, 即此时不确定这个bean是否存在,但持有beanName,等到运行时再去获取
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
// 标记为已经使用了工厂bean
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
// 将 sqlSessionFactory 作为属性添加到bean定义中
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
// 6. 添加sqlSessionTemplate,如果beanName存在,就引入beanName,如果sqlSessionTemplate不为空,就引入sqlSessionTemplate
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
definition.getPropertyValues().add("sqlSessionTemplate",new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
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);
}
}
- 遍历所有的bean定义
- 将实际类保存下来
// mybatis 代理类的构造器函数如下,会将被代理的类保存下来 public MapperFactoryBean(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; }
- 如果sqlSessionFactoryBeanName不为空就将他引入,如果sqlSessionFactory不为空就将他引入
- 如果sqlSessionTemplateBeanName存在,就引入beanName,如果sqlSessionTemplate不为空,就引入sqlSessionTemplate
由以上过程发现,是将所有扫描到的候选bean定义,代理成了 MapperFactoryBean
类,并为其添加了两个属性
- sqlSessionFactory
- sqlSessionTemplate
并没有涉及到扫描的过程
3. 为什么会扫描两遍?重新看父类扫描过程
扫描函数:org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan
- 对特定的包执行扫描逻辑,返回bean定义
/**
* Perform a scan within the specified base packages,
* returning the registered bean definitions.
* <p>This method does <i>not</i> register an annotation config processor
* but rather leaves this up to the caller.
* @param basePackages the packages to check for annotated classes
* @return set of beans registered if any for tooling registration purposes (never {@code null})
*/
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) {
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)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}