原本只是由於【sqlSessionTemplate重複注入找解決方法】,在排查過程當中又對ClassPathMapperScanner 到底幹了什麼產生了疑問,爲何是MapperFactoryBean構建Mapper ? 爲何會有屢次處理sqlSessionTemplete?java
主要功能是掃描出路徑下的Mapper對象被將其將BeanDefinition保存在全局的BeanDefinitionRegistry (DefaultListableBeanFactory)中。sql
從上篇博客章節【@Mapper與@MapperScan如何正確使用】中瞭解到:
MapperScannerConfigurer、@MapperScan import的MapperScannerRegistrar、自動掃描@Mapper的AutoConfiguredMapperScannerRegistrar都是建立了ClassPathMapperScanner(默認路徑爲啓動Applicaiton類的包路徑)。app
下文是MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor:源碼分析
發現上篇文章問題中說到的SqlSessionFactory、SqlSessionTemplate、MapperFactoryBean都出現了。測試
debug過程當中發現:
doScan方法在經過basePackage執行findCandidateComponens方法時
使用了ClassPathMapperScanner本身的isCandidateComponent方法對返回結果集斷定時: 只是斷定bean的定義描述是不是接口this
若是指定的掃描路徑basePackage含有其餘接口類的話,doScan最終返回的Set<BeanDefinitionHolder>中會含有不少不相干的接口信息。
spa
因此:.net
最終:debug
調用BeanDefinitionRegistry的registerBeanDefinition方法將這個BeanDefinition保存在DefaultListableBeanFactory(implements BeanDefinitionRegistry)的Map<String, BeanDefinition> beanDefinitionMap 中!
3d
填充BeanDefinition的屬性內容。
設置自動注入的類型AutowireMode!很關鍵!並且:若是有設置sqlSessionFactory/sqlSessionTemplate 則不設置AutowireMode, 默認是AUTOWIRE_NO!(控制是否自動注入)
最終的MutablePropertyValues屬性值:
那問題就來了: 爲何在上篇博客中, createBean時 AbstractAutowireCapableBeanFactory.populateBean方法最終處理獲得的屬性多了sqlSessionFactory、sqlSessionTemplate?
RootBeanDefinition在進入populateBean方法時確實還保持着和MapperScan時一致的屬性集合。
注意:由於在掃描註冊時設置了AUTOWIRE_BY_TYPE,因此會執行autowireByType方法
在autowireByType方法中經過unsatisfiedNonSimpleProperties方法來得到須要注入的屬性字段
在方法進入時PropertyValues仍是與掃描時一致。關鍵在於:PropertyDescriptor[]中有MapperFactoryBean的全部成員屬性的描述信息包括sqlSessionTemplate、sqlSessionFactory!
在輪詢PropertyDescriptor[] 時斷定getWriteMethod()時: 斷定有setSqlSessionFactory()和 setSqlSessionTemplate() 。
因此返回的結果集包涵了二者!!
接着又回到了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); } }