本文主要是介绍SpringBoot 源码解析5:ConfigurationClassPostProcessor整体流程和@ComponentScan源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
SpringBoot 源码解析5:ConfigurationClassPostProcessor整体流程和@ComponentScan源码分析
- 1. 知道以下几点,读ConfigurationClassPostProcessor源码会更轻松
- 2. 源码解析 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
- 2.1 ConfigurationClassPostProcessor#processConfigBeanDefinitions
- 2.1.1 ConfigurationClassUtils#checkConfigurationClassCandidate
- 2.1.2 ConfigurationClassUtils#isConfigurationCandidate
- 2.2 ConfigurationClassParser#parse
- 2.2.1 ConfigurationClassParser#processConfigurationClass
- 2.2.2 ConfigurationClassParser#doProcessConfigurationClass
- 3. @ComponentScan 源码分析
- 3.1 ComponentScanAnnotationParser#parse
- 3.2 ClassPathBeanDefinitionScanner#doScan
- 3.3 ClassPathScanningCandidateComponentProvider#scanCandidateComponents
- 3.4 ClassPathScanningCandidateComponentProvider#isCandidateComponent
- 3.5 @ComponentScan源码总结
- 4. TODO 其他注解
1. 知道以下几点,读ConfigurationClassPostProcessor源码会更轻松
- 配置类后置处理器ConfigurationClassPostProcessor,是对Spring注解式配置支持的核心,负责对@Component、@ComponentScan、@Import等注解的解析,将BeanDefinition注册到beanFactory。
- ConfigurationClassPostProcessor是在AnnotationConfigServletWebServerApplicationContext创建的时候注册的。参考 SpringBoot 源码解析2:启动流程
- BeanDefinitionRegistryPostProcessor实现了BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor。ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry在刷新容器的时候AbstractApplicationContext#invokeBeanFactoryPostProcessors方法中调用。参考 SpringBoot 源码解析4:refresh 方法解析
2. 源码解析 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {int registryId = System.identityHashCode(registry);if (this.registriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);}if (this.factoriesPostProcessed.contains(registryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + registry);}this.registriesPostProcessed.add(registryId);processConfigBeanDefinitions(registry);
}
registry其实就是就是beanFactory,一个beanFactory只会处理一次
2.1 ConfigurationClassPostProcessor#processConfigBeanDefinitions
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {//存放有@Configuration的BeanDefinitionHolderList<BeanDefinitionHolder> configCandidates = new ArrayList<>();//获取到手动注册的BeanDefinition,在此之前已经将启动类注册了,请参考springBoot启动流程String[] candidateNames = registry.getBeanDefinitionNames();for (String beanName : candidateNames) {BeanDefinition beanDef = registry.getBeanDefinition(beanName);//判断BeanDefinition是否存在ConfigurationClassPostProcessor.configurationClass属性if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);}}// 不存在ConfigurationClassPostProcessor.configurationClass,就会配置此属性。// 配置里@Configuration就会返回true,才会放入到候选配置里面else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// Return immediately if no @Configuration classes were found// 没有发现@Configuration,就会立马返回if (configCandidates.isEmpty()) {return;}// Sort by previously determined @Order value, if applicable// 按照@Order排序configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});// Detect any custom bean name generation strategy supplied through the enclosing application context// 探测有没有自定义的BeanName生成器,如果有的话就使用自定义的beanName生成器SingletonBeanRegistry sbr = null;if (registry instanceof SingletonBeanRegistry) {sbr = (SingletonBeanRegistry) registry;if (!this.localBeanNameGeneratorSet) {BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);if (generator != null) {this.componentScanBeanNameGenerator = generator;this.importBeanNameGenerator = generator;}}}if (this.environment == null) {this.environment = new StandardEnvironment();}// Parse each @Configuration class// 创建解析器,解析每一个有@Configuration的类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 {// 解析有@Configuration的BeanDefinitionparser.parse(candidates);// 对Configuration和Bean注解进行了校验parser.validate();Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 此时有一些BeanDefinition没有被注册,比如@Bean、@Import中的ImportBeanDefinitionRegistrarthis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);candidates.clear();// 这是在一个循环里面,判断bean工厂在解析前和解析后是否有新注册的BeanDefinition// 如果有新注册的BeanDefinition,那么这些BeanDefinition也有可能是配置类,就会去解析这些BeanDefinitionif (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}}while (!candidates.isEmpty());// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classesif (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());}if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {// Clear cache in externally provided MetadataReaderFactory; this is a no-op// for a shared cache since it'll be cleared by the ApplicationContext.((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();}
}
- 获取到所有注册到的BeanDefinition,通过ConfigurationClassPostProcessor.configurationClass属性,判断BeanDefinition有没有被处理过。下面会讲ConfigurationClassUtils#checkConfigurationClassCandidate。
- 如果没有处理,那么就会调用 ConfigurationClassUtils#checkConfigurationClassCandidate判断当前的BeanDefinition是否为配置类。只有当它为配置类,后面的配置类解析器才会对它进行解析。
- 兼容了自定义的Bean的名称生成器,可以使用用户自定义的生成器。
- 创建配置类解析器ConfigurationClassParser,对已排好序的BeanDefinitionHolder进行解析。
- 对Configuration和Bean注解进行一些校验。
- 判断bean工厂在解析前和解析后是否有新注册的BeanDefinition。如果有新注册的BeanDefinition,这些BeanDefinition有可能是配置类,那么就会通过ConfigurationClassParser去解析这些BeanDefinition。
2.1.1 ConfigurationClassUtils#checkConfigurationClassCandidate
校验当前的BeanDefinition是否为配置类
public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {String className = beanDef.getBeanClassName();if (className == null || beanDef.getFactoryMethodName() != null) {return false;}AnnotationMetadata metadata;if (beanDef instanceof AnnotatedBeanDefinition &&className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {// Can reuse the pre-parsed metadata from the given BeanDefinition...metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();}else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {// Check already loaded Class if present...// since we possibly can't even load the class file for this Class.// 这些类型Spring不可作为配置类Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||BeanPostProcessor.class.isAssignableFrom(beanClass) ||AopInfrastructureBean.class.isAssignableFrom(beanClass) ||EventListenerFactory.class.isAssignableFrom(beanClass)) {return false;}metadata = AnnotationMetadata.introspect(beanClass);}else {try {MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);metadata = metadataReader.getAnnotationMetadata();}catch (IOException ex) {if (logger.isDebugEnabled()) {logger.debug("Could not find class file for introspecting configuration annotations: " +className, ex);}return false;}}// 判断获取Configuration注解的属性Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {// Configuration 注解的proxyBeanMethods属性为true,则为BeanDefinition为full,会被代理beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}// 当前的BeanDefinition"能作为配置类",配置属性为lite,不被代理else if (config != null || isConfigurationCandidate(metadata)) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);}else {// Configuration注解不存在return false;}// It's a full or lite configuration candidate... Let's determine the order value, if any.Integer order = getOrder(metadata);if (order != null) {beanDef.setAttribute(ORDER_ATTRIBUTE, order);}return true;
}
- 判断了BeanFactoryPostProcessor、BeanPostProcessor、AopInfrastructureBean、EventListenerFactory类型不能作为配置类。
- 如果当前BeanDefintion没有@Configuration,就不能作为配置类。
- 如果有@Configuration并且proxyBeanMethods属性为true,能作为配置类,就会配置为full。为full的会被代理。
- 如果有@Configuration并且proxyBeanMethods属性为false,并且判断 “能作为配置类”,就会配置为lite。lite不会被代理。
- 这里简单提一下@Configuration代理过程,如果Configuration的proxyBeanMethods属性为true,那么当前的BeanDefinition为full,就会在ConfigurationClassPostProcessor#enhanceConfigurationClasses会创建代理类,从而创建代理对象。代理对象在调用beanMethod(@Bean方法)时,会被BeanMethodInterceptor拦截器拦截。代理拦截器的逻辑是这样的:在第一次调用beanMethod完毕后生成bean之后,会根据类名和方法名称生成一个缓存,第二次生成调用同一个beanMethod,会直接从缓存中取,而不是调用beanMethod去创建对象。
- 那么,什么样"能作为配置类"呢?
2.1.2 ConfigurationClassUtils#isConfigurationCandidate
private static final Set<String> candidateIndicators = new HashSet<>(8);static {candidateIndicators.add(Component.class.getName());candidateIndicators.add(ComponentScan.class.getName());candidateIndicators.add(Import.class.getName());candidateIndicators.add(ImportResource.class.getName());
}public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {// Do not consider an interface or an annotation...if (metadata.isInterface()) {return false;}// Any of the typical annotations found?for (String indicator : candidateIndicators) {if (metadata.isAnnotated(indicator)) {return true;}}// Finally, let's look for @Bean methods...try {return metadata.hasAnnotatedMethods(Bean.class.getName());}catch (Throwable ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);}return false;}
}
能作为配置类的条件:首先,接口肯定是不能作为配置类的。类上有Component、ComponentScan、Import、ImportResource,或者方法中有@Bean注解的,都能作为配置类。
2.2 ConfigurationClassParser#parse
public void parse(Set<BeanDefinitionHolder> configCandidates) {for (BeanDefinitionHolder holder : configCandidates) {BeanDefinition bd = holder.getBeanDefinition();try {if (bd instanceof AnnotatedBeanDefinition) {parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());}else {parse(bd.getBeanClassName(), holder.getBeanName());}}catch (BeanDefinitionStoreException ex) {throw ex;}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);}}this.deferredImportSelectorHandler.process();
}
在解析完配置类之后,才会对deferredImportSelector进行处理。
2.2.1 ConfigurationClassParser#processConfigurationClass
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {processConfigurationClass(new ConfigurationClass(metadata, beanName));
}protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {// 支持Conditional注解if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {return;}ConfigurationClass existingClass = this.configurationClasses.get(configClass);if (existingClass != null) {if (configClass.isImported()) {if (existingClass.isImported()) {existingClass.mergeImportedBy(configClass);}// Otherwise ignore new imported config class; existing non-imported class overrides it.return;}else {// Explicit bean definition found, probably replacing an import.// Let's remove the old one and go with the new one.this.configurationClasses.remove(configClass);this.knownSuperclasses.values().removeIf(configClass::equals);}}// Recursively process the configuration class and its superclass hierarchy.SourceClass sourceClass = asSourceClass(configClass);do {sourceClass = doProcessConfigurationClass(configClass, sourceClass);}while (sourceClass != null);this.configurationClasses.put(configClass, configClass);
}
- configurationClasses中存放着已解析的配置信息,如果没有加载,则调用ConfigurationClassParser#doProcessConfigurationClass方法解析配置类,解析完毕之后放入到configurationClasses。
- 如果已解析并且importedBy不为空,则合并importedBy。如果不是Import,则清除缓存,重新解析。
2.2.2 ConfigurationClassParser#doProcessConfigurationClass
@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {// 处理@Component注解,处理当前类声明的类DeclaredClasse,因为当前类的内部类也有可能是配置类if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass);}// Process any @PropertySource annotations// 处理@PropertySource 注解for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotations// 处理@ComponentScan注解 Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (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());}}}}// Process any @Import annotations// 处理@Import注解processImports(configClass, sourceClass, getImports(sourceClass), true);// Process any @ImportResource annotations// 处理@ImportResource注解AnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// Process individual @Bean methods// 处理@Bean注解Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfacesprocessInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;
}
- 此方法返回的是父类的资源,如果没有父类,说明类当前类已解析完毕。
- 对Component、PropertySource、ComponentScan、Import 、ImportResource、Bean等注解进行了解析。
- 我们最关心的是@ComponentScan和@Component,下面我们就对它们进行源码分析。
3. @ComponentScan 源码分析
在创建ConfigurationClassParser的时候,就创建了componentScanParser。
public ConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry) {this.metadataReaderFactory = metadataReaderFactory;this.problemReporter = problemReporter;this.environment = environment;this.resourceLoader = resourceLoader;this.registry = registry;this.componentScanParser = new ComponentScanAnnotationParser(environment, resourceLoader, componentScanBeanNameGenerator, registry);this.conditionEvaluator = new ConditionEvaluator(registry, environment, resourceLoader);
}
3.1 ComponentScanAnnotationParser#parse
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {// 创建扫描器,默认配置了三个includeFilters。Component、ManagedBean、Named。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));}//如果componentScan没有配置basePackages和basePackageClasses属性,那么就取声明@ComponentScan的类所对应的包名if (basePackages.isEmpty()) {basePackages.add(ClassUtils.getPackageName(declaringClass));}scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {@Overrideprotected boolean matchClassName(String className) {return declaringClass.equals(className);}});// 扫描包return scanner.doScan(StringUtils.toStringArray(basePackages));
}
- 此方法只是对扫描器进行了配置,扫描前的准备工作。
- ComponentScan扫描的时候有两种过滤器,excludeFilters比includeFilters优先级高:
2.1. includeFilters:includeFilters匹配到的会注册到bean工厂。
2.2. excludeFilters:excludeFilters匹配到的不会注册到bean工厂。 - ClassPathBeanDefinitionScanner如果使用默认的过滤器,就会添加三个注解式的includeFilter:Component、ManagedBean、Named。
protected void registerDefaultFilters() {this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.}try {this.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.}
}
- 如果@componentScan没有配置basePackages和basePackageClasses属性,那么就取声明@ComponentScan的class所对应的包名。
3.2 ClassPathBeanDefinitionScanner#doScan
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) {//获取到basePackage下面所有的beanDefinitionSet<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {//解析Scope注解,默认singleton不代理ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());//通过bean的名称生成器生成beanName,默认的是AnnotationBeanNameGenerator。解析Component、ManagedBean、Named的value属性。如果以上注解属性,则AnnotationBeanNameGenerator#buildDefaultBeanName创建默认的beanName,如果类名的前两个字母是大写,则是类的名称,否则就将首字母变成小写。String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {//处理beanDefinition,对BeanDefinition设置了默认的属性postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {//解析公共的注解Lazy、Primary、DependsOn、Role、Description,对BeanDefinition的属性进行修改。AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);//Scope代理处理,如果需要代理,则已经注册了原始的BeanDefinition,返回的是代理的BeanDifinition。definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);//注册BeanDefinitionregisterBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}
- 获取到basePackage下面所有的资源,并且解析成BeanDefinition。
- 对@Scope注解进行解析,判断是否需要代理,默认singleton不代理。
- 通过bean的名称生成器生成beanName,默认的是AnnotationBeanNameGenerator。解析Component、ManagedBean、Named的value属性。如果以上注解属性,则AnnotationBeanNameGenerator#buildDefaultBeanName创建默认的beanName,如果类名的前两个字母是大写,则是类的名称,否则就将首字母变成小写。
- 处理beanDefinition,对BeanDefinition设置了默认的属性。
- 解析公共的注解Lazy、Primary、DependsOn、Role、Description,对BeanDefinition的属性进行修改。
- Scope代理处理,如果需要代理,则已经注册了原始的BeanDefinition,返回的是代理的BeanDifinition。
- 注册BeanDefinition。
3.3 ClassPathScanningCandidateComponentProvider#scanCandidateComponents
如何扫描资源,并且生成BeanDefinition的?
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);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}if (resource.isReadable()) {try {//获取元数据读取器,MetadataReader 负责读取资源里面的字节码内容MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);//通过ComponentScan的过滤器,去扫描当前资源是否匹配。if (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setResource(resource);sbd.setSource(resource);if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}}else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}}catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}else {if (traceEnabled) {logger.trace("Ignored because not readable: " + resource);}}}}catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;
}
1.由上述可知,componentScan解析器配置了三个默认的includeFilter。所以,有Component注解的class会被扫描到,并且生成BeanDefinition。
2. 获取到当前包下面所有的资源,通过includeFilter和excludeFilter判断是否为候选的spring组件。如果是,就会生成BeanDefinition。其中excludeFilter的优先级高。
3. 最终返回所有获选的BeanDefinition。BeanDefinition中存放了元数据读取器MetadataReader,元数据读取器负责读取资源中类的信息,注解、字段、方法等。
3.4 ClassPathScanningCandidateComponentProvider#isCandidateComponent
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;
}
匹配规则如下
- 优先匹配excludeFilter,再匹配includeFilter。
- 只要有一个excludeFilter匹配到,就不会添加到候选的BeanDefinition中。
- includeFilter匹配到了才会添加到候选的BeanDefinition,否则为非候选。
3.5 @ComponentScan源码总结
- ClassPathBeanDefinitionScanner对负责扫描classPath下面的资源文件。扫描的规则是通过includeFilters和excludeFilters完成的,其中注册了三个默认的注解过滤器@Component、@ManagedBean、@Named。
- 元数据读取器MetadataReader:有了资源文件,那么也需要一个元数据读取器去读取资源中的内容,比如判断是否有@Component注解,这个功能就是由元数据读取器完成的。
- 最终将资源文件和可读取资源文件的MetadataReader封装城BeanDefinition,注册到了DefaultListableBeanFactory#beanDefinitionMap。
4. TODO 其他注解
其他注解后面在写,先写完主流程。
这篇关于SpringBoot 源码解析5:ConfigurationClassPostProcessor整体流程和@ComponentScan源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!