Spring @ComponentScan注解掃描組件原理
本文將通過(guò)閱讀spring源碼,分析@ComponentScan注解掃描組件的原理。
和@Bean注解一樣,@ComponentScan注解也是通過(guò)ConfigurationClassPostProcessor后置處理器完成掃描工作的。
在《Spring-@Bean注解源碼分析》中,詳細(xì)介紹了spring注冊(cè)、調(diào)用ConfigurationClassPostProcessor后置處理器的流程,本文不再重復(fù)記錄。
ConfigurationClassPostProcessor類
ConfigurationClassPostProcessor實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口,spring容器在refresh過(guò)程中,會(huì)調(diào)用所有BeanDefinitionRegistryPostProcessor處理器的postProcessBeanDefinitionRegistry方法:
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
processConfigBeanDefinitions(registry);
}
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// ...
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanBeanNameGenerator, registry);
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
do {
parser.parse(candidates);
parser.validate();
Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);
// Read the model and create bean definitions based on its content
if (this.reader == null) {
this.reader = new ConfigurationClassBeanDefinitionReader(
registry, this.sourceExtractor, this.resourceLoader, this.environment,
this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
// ...重點(diǎn)邏輯在parser.parse(candidates)處,作用是解析@Configuration類。
Parse each @Configuration class
parser.parse(candidates);
最終調(diào)用到:
protected final SourceClass doProcessConfigurationClass(
ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
throws IOException {
// ...
// 1. 解析@ComponentScan注解元數(shù)據(jù)
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
// 2. 判斷標(biāo)注了@ComponentScan注解且不skip該類
if (!componentScans.isEmpty() &&
!this.conditionEvaluator.shouldSkip(
sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
// 可能標(biāo)注多個(gè)@ComponentScan注解
for (AnnotationAttributes componentScan : componentScans) {
// 3. 通過(guò)@ComponentScan解析組件BeanDefinition集
Set<BeanDefinitionHolder> scannedBeanDefinitions =
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
// Check the set of scanned definitions for any further config classes and
// parse recursively if needed
// 4. 遞歸解析
for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
if (bdCand == null) {
bdCand = holder.getBeanDefinition();
}
if (ConfigurationClassUtils
.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
parse(bdCand.getBeanClassName(), holder.getBeanName());
}
}
}
}
// ...
}
重點(diǎn)在第3點(diǎn),通過(guò)@ComponentScan解析組件BeanDefinition集:
this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
componentScanParser是ComponentScanAnnotationParser類型對(duì)象,parse方法解析ComponentScan注解,掃描組件。
ComponentScanAnnotationParser類
parse方法解析ComponentScan注解,掃描組件:
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
BeanUtils.instantiateClass(generatorClass));
ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
scanner.setScopedProxyMode(scopedProxyMode);
} else {
Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
}
scanner.setResourcePattern(componentScan.getString("resourcePattern"));
for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addIncludeFilter(typeFilter);
}
}
for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
for (TypeFilter typeFilter : typeFiltersFor(filter)) {
scanner.addExcludeFilter(typeFilter);
}
}
boolean lazyInit = componentScan.getBoolean("lazyInit");
if (lazyInit) {
scanner.getBeanDefinitionDefaults().setLazyInit(true);
}
Set<String> basePackages = new LinkedHashSet<>();
String[] basePackagesArray = componentScan.getStringArray("basePackages");
for (String pkg : basePackagesArray) {
String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
Collections.addAll(basePackages, tokenized);
}
for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
if (basePackages.isEmpty()) {
basePackages.add(ClassUtils.getPackageName(declaringClass));
}
scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
@Override
protected boolean matchClassName(String className) {
return declaringClass.equals(className);
}
});
return scanner.doScan(StringUtils.toStringArray(basePackages));
}ClassPathBeanDefinitionScanner類
doScan方法掃描指定包下面的組件:
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
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);
// 注冊(cè)到容器
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
} else {
return scanCandidateComponents(basePackage);
}
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
for (Resource resource : resources) {
if (resource.isReadable()) {
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
// 判斷是否是需要掃描的spring組件,遍歷excludeFilters和includeFilters集來(lái)進(jìn)行判斷
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
}
} catch (Throwable ex) {
throw new BeanDefinitionStoreException("");
}
}
}
} catch (IOException ex) {
throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
}
return candidates;
}到此這篇關(guān)于Spring @ComponentScan注解掃描組件原理的文章就介紹到這了,更多相關(guān)Spring @ComponentScan注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 基于@ComponentScan注解及其XML配置方式
- 關(guān)于@ComponentScan注解的用法及作用說(shuō)明
- SpringBoot中@ComponentScan注解過(guò)濾排除不加載某個(gè)類的3種方法
- @AutoConfigurationPackage與@ComponentScan注解區(qū)別
- spring?boot自動(dòng)裝配之@ComponentScan注解用法詳解
- Spring @ComponentScan注解使用案例詳細(xì)講解
- Spring?component-scan?XML配置與@ComponentScan注解配置
- 基于ComponentScan注解的掃描范圍及源碼解析
相關(guān)文章
Shiro安全框架的主要組件及認(rèn)證過(guò)程簡(jiǎn)介
這篇文章主要介紹了Shiro安全框架的主要組件及認(rèn)證過(guò)程簡(jiǎn)介,Shiro?是一個(gè)強(qiáng)大靈活的開源安全框架,可以完全處理身份驗(yàn)證、授權(quán)、加密和會(huì)話管理,本文就來(lái)介紹一下此框架的核心組成,需要的朋友可以參考下2023-08-08
Java 8實(shí)現(xiàn)任意參數(shù)的單鏈表
這篇文章主要為大家詳細(xì)介紹了Java 8實(shí)現(xiàn)任意參數(shù)的單鏈表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-10-10
示例解析java面向?qū)ο缶幊谭庋b與訪問(wèn)控制
這篇文章主要為大家介紹了java封裝與訪問(wèn)控制的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
JAVA實(shí)現(xiàn)讀取txt文件內(nèi)容的方法
本篇文章主要介紹了JAVA實(shí)現(xiàn)讀取txt文件內(nèi)容的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
idea 實(shí)現(xiàn)搜索jdk中的類和包操作
這篇文章主要介紹了idea 實(shí)現(xiàn)搜索jdk中的類和包操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02

