本来只是因为【sqlSessionTemplate重复注入找解决方法】,在排查过程中又对ClassPathMapperScanner 到底干了什么产生了疑问,为什么是MapperFactoryBean构建Mapper ? 为什么会有多次处理sqlSessionTemplete?
结论
- ClassPathMapperScanner
- 决定了对于mapper对象使用mapperFactoryBean来wrapper,并确定了mapperFactoryBean需要注入的属性值对PropertyValues。
- ClassPathMapperScanner 控制了是否需要设置预设的 sqlSessionFactory、sqlSessionTemplate,可以用做多数据源切换的一种实现方案(另外一种:AbstractRoutingDataSource)。
如果设置了,AutowireMode默认是AUTOWIRE_NO ;否则是 AUTOWIRE_BY_TYPE;
- 在AbstractAutowireCapableBeanFactory的populateBean方法中将判定AUTOWIRE_BY_TYPE
- 是: 执行autowireByType方法 。根据类的成员变量属性描述及setXXXX来确定mapperFactoryBean需要注入哪些实例,如果容器中存在或者可以创建这个实例,那就会注入!
- 否: 跳过autowireByType方法。 直接按在ClassPathMapperScanner 确定的PropertyValues 直接设置mapperFactoryBean属性值。
- 各种MapperScan的类继承的接口入参中都包含BeanDefinitionRegistry , 将在容器初始化过程中被注入! 实际上DefaultListableBeanFactory implements BeanDefinitionRegistry 保存bean的定义信息。
ClassPathMapperScanner
主要功能是扫描出路径下的Mapper对象被将其将BeanDefinition保存在全局的BeanDefinitionRegistry (DefaultListableBeanFactory)中。
从上篇博客章节【@Mapper与@MapperScan如何正确使用】中了解到:
MapperScannerConfigurer、@MapperScan import的MapperScannerRegistrar、自动扫描@Mapper的AutoConfiguredMapperScannerRegistrar都是创建了ClassPathMapperScanner(默认路径为启动Applicaiton类的包路径)。
下文是MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor:
源码分析
发现上篇文章问题中说到的SqlSessionFactory、SqlSessionTemplate、MapperFactoryBean都出现了。
doScan方法
在断点过程中发现:
doScan方法在通过basePackage执行findCandidateComponens方法时使用了ClassPathMapperScanner的isCandidateComponent方法对返回结果集判定时: 只是判定bean的定义描述是否是接口
所以如果指定的扫描路径basePackage含有其他接口类的话,doScan最终返回的BeanDefinitionHolder集合中会含有很多不相干的接口信息。
最后:
调用BeanDefinitionRegistry的registerBeanDefinition方法将这个BeanDefinition保存在DefaultListableBeanFactory(implements BeanDefinitionRegistry)的Map<String, BeanDefinition> beanDefinitionMap 中!
processBeanDefinitions方法
填充BeanDefinition的属性内容。
- 设置beanClass为mapperFactoryBean。(这也证实了对于每一个Mapper都会通过mapperFactoryBean来构建)
- 判定是否需要设置 sqlSessionFactory、sqlSessionTemplate; 如果有设置的话explicitFactoryUsed = true.(此时测试用例的执行都没有设置这些值, 如果想使用多数据源,可以在MapperScan时指定扫描Dao路径的同时增加对sqlSessionFactory的指定)
-
设置自动注入的类型AutowireMode!很关键!而且:如果有设置sqlSessionFactory/sqlSessionTemplate 则不设置AutowireMode, 默认是AUTOWIRE_NO!(控制是否自动注入)
最终的MutablePropertyValues属性值:
AbstractAutowireCapableBeanFactory
那问题就来了: 为什么在上篇博客中, createBean时 AbstractAutowireCapableBeanFactory.populateBean方法最终处理得到的属性多了sqlSessionFactory、sqlSessionTemplate?
RootBeanDefinition在进入populateBean方法时确实还保持着和MapperScan时一致的属性集合。
注意:因为在扫描注册时设置了AUTOWIRE_BY_TYPE,所以会执行autowireByType方法
在autowireByType方法中通过unsatisfiedNonSimpleProperties方法来获得需要注入的属性字段
unsatisfiedNonSimpleProperties方法
在方法进入时PropertyValues还是与扫描时一致。关键在于:PropertyDescriptor[]中有MapperFactoryBean的所有成员属性的描述信息包括sqlSessionTemplate、sqlSessionFactory!
在轮询PropertyDescriptor[] 时判定getWriteMethod()时: 判定有setSqlSessionFactory()和 setSqlSessionTemplate() 。
所以返回的结果集包涵了两者!!
接着又回到了autowireByType方法的执行。
autowireByType 方法
核心是: 通过处理好的propertyNames来注入真实实例
跟踪断点发现:
获取到了容器中已有的defaultSqlSessionFactory的实例。判定不等于空填入MutablePropertyValues集合中
无法获取SqlSessionTemplate实例就不置入MutablePropertyValues集合中
所以: 最终回到populateBean时就确定好了PropertyValues!此时将只会执行setSqlSessionFactory!
反向验证
测试时增入自己显示声明 @Bean SqlSessionTemplate:
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
断点发现它又有实例了!所以 MapperFactoryBean 先setSqlSessionFactory再 setSqlSessionTemplate~
其他关键源码
MapperScannerRegistrar implements ImportBeanDefinitionRegistrar:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar:
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
logger.debug("Searching for mappers annotated with @Mapper");
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
for (String pkg : packages) {
logger.debug("Using auto-configuration base package '{}'", pkg);
}
}
scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(packages));
} catch (IllegalStateException ex) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
}
}