Spring @ComponentScans註解的解析
阿新 • • 發佈:2021-01-20
@ComponentScans
註解是組成@SpringBootApplication
註解的之一。主要用於掃描專案中的元件,比如@Compoent``@Service
等等,預設掃描路徑是應用程式啟動類的同級路徑,可以新增應用的自定義路徑。
@ComponentScans
註解是在 @Configuration
前被解析。
@ComponentScans
註解是被ComponentScanAnnotationParser#parse()
方法解析的。
ComponentScanAnnotationParser
先簡單瞭解ComponentScanAnnotationParser這個類。
成員變數:
- Environment environment 系統環境
- ResourceLoader resourceLoader 資源載入器
- BeanNameGenerator beanNameGenerator beanName生成策略
- BeanDefinitionRegistry registry bean工廠(DefaultListableBeanFactory)
核心方法
- parse(AnnotationAttributes componentScan, final String declaringClass)
componentScan是@ComponentScan
註解的11個屬性。
declaringClass是系統啟動類的全限定名。

parse()
ClassPathBeanDefinitionScanner
類,在方法的最後會呼叫ClassPathBeanDefinitionScanner#doScan()
方法掃描專案。
其中doScan()方法的傳參是掃描路徑,預設是啟動類的路徑,也可以自定義,支援多個路徑
//basePackages掃描路徑,如:com.kafkaproducer 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) { //1.這裡會掃描路徑下的所有類。如:/Users/XX/kafka-test/kafkaproducer/target/classes/com/kafkaproducer/KafkaProducerTest.class //2.把資源封裝成一個SimpleMetadataReader //3.根據@ComponentScan的filter判斷資源是否滿足條件 //4. 滿足步驟3的資源,再進行一次篩選。最終篩選出來的類的註解都包含了@Component 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); //對掃描出來的candidate的BeanDefinition,設定一些引數。 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } if (candidate instanceof AnnotatedBeanDefinition) { AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //檢查beanName在登錄檔中是否存在(可能會有衝突) //true:可以註冊;false:bean已存在,(beanName相同,beanDefinition也相同) if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); //是否通過代理建立 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //註冊beanDefinition(登錄檔快取) registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
總結
掃描註解@ComponentScans
預設會根據應用程式啟動類所在的根路徑為預設掃描路徑(參考:classpath:com/kafkaproducer/**/.class),也可以通過指定其他路徑(比如專案中的DAO層是通過jar包的形式引入進來的,可以指定路徑掃描DAO的元件)。負責掃描的類--ClassPathBeanDefinitionScanner
會掃描專案的所有class檔案,再經過Filter過濾,得到所有被@Component註解修飾的類。最後把相關類的beanDefiniton存入快取中。