标签:metadata parser register 如何 类型 ann property mes config
(SpringBoot 版本:2.2.2.RELEASE)
可以说 @Configuration 是 SpringBoot 配置的基石,自然 @Configuration 类的处理是很有必要研究的。
@Configuration 类的处理是由 ConfigurationClassPostProcessor 来处理的。
以如下工程结构来分析:
// 处理 @Configuration 类的导入,将 bean 注册到容器中
org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions
1 public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
2 List configCandidates = new ArrayList();
3 String[] candidateNames = registry.getBeanDefinitionNames();
4
5 // 1. 检索所有的配置类
6 for (String beanName : candidateNames) {
7 BeanDefinition beanDef = registry.getBeanDefinition(beanName);
8 if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
9 if (logger.isDebugEnabled()) {
10 logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
11 }
12 }
13 else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
14 configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
15 }
16 }
17
18 // Return immediately if no @Configuration classes were found
19 if (configCandidates.isEmpty()) {
20 return;
21 }
22
23 // Sort by previously determined @Order value, if applicable
24 // 2. 对所有的配置类进行排序。(按 @Order 的顺序排,@Configuration 类的顺序决定了后面的处理顺序)
25 configCandidates.sort((bd1, bd2) -> {
26 int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
27 int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
28 return Integer.compare(i1, i2);
29 });
30
31 // Detect any custom bean name generation strategy supplied through the enclosing application context
32 SingletonBeanRegistry sbr = null;
33 if (registry instanceof SingletonBeanRegistry) {
34 sbr = (SingletonBeanRegistry) registry;
35 if (!this.localBeanNameGeneratorSet) {
36 BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
37 AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
38 if (generator != null) {
39 this.componentScanBeanNameGenerator = generator;
40 this.importBeanNameGenerator = generator;
41 }
42 }
43 }
44
45 if (this.environment == null) {
46 this.environment = new StandardEnvironment();
47 }
48
49 // Parse each @Configuration class
50 ConfigurationClassParser parser = new ConfigurationClassParser(
51 this.metadataReaderFactory, this.problemReporter, this.environment,
52 this.resourceLoader, this.componentScanBeanNameGenerator, registry);
53
54 Set candidates = new LinkedHashSet(configCandidates);
55 Set alreadyParsed = new HashSet(configCandidates.size());
56 do {
57 // 3. 解析所有的配置类,其中包括 @PropertySource、@ComponentScan、@Import、@ImportResource、@Bean 等的处理,这些 bean 全部都会被封装成一个 ConfigurationClass 实例,包括普通 bean 和 @Configuration 标记的类
58 // 需要注意的是,SpringBoot 的启动类(这里指:PlainApplication)是最先开始解析的
59 // 特别需要注意的是:@ComponentScan 扫描出来的 bean 会在这一步中优先注册到容器中(DefaultListableBeanFactory#beanDefinitionMap)
60 // 有些 bean (比如:serviceA)是带条件注册的(指:@ConditionalOnXxx),比如条件是 @ConditionalOnBean(x.y),则有可能达不到预期
61 // 达不到预期的场景:x.y 这个 bean 不是 @ComponentScan 扫描出来的 bean,而是通过 @Import、@ImportResource、@Bean 等方式产生的,则 serviceA 不会被注册到容器中。
62 // 那么,如何修正或者规避这类问题呢?答案是将 x.y 这个 bean 使用 @ComponentScan 的方式扫描进行注册
63 parser.parse(candidates);
64 parser.validate();
65
66 Set configClasses = new LinkedHashSet(parser.getConfigurationClasses());
67 configClasses.removeAll(alreadyParsed);
68
69 // Read the model and create bean definitions based on its content
70 if (this.reader == null) {
71 this.reader = new ConfigurationClassBeanDefinitionReader(
72 registry, this.sourceExtractor, this.resourceLoader, this.environment,
73 this.importBeanNameGenerator, parser.getImportRegistry());
74 }
75 // 4. 将所有的 ConfigurationClass 实例时行加载,即将定义的 bean 注册到容器中
76 // a. 判断是否要 skip
77 // b. 处理 import 导入的 @Configuration 类,将它注册到容器中。beanName 默认为类的全限定名,如:com.kvn.other.Config1
78 // c. 将 ConfigurationClass 中的 BeanMethod 类型的 bean(即 @Bean 标记的方法)注册到容器中
79 // d. 将 @ImportResources 中的 bean 注册到容器中
80 // e. 注册 ImportBeanDefinitionRegistrar 接口中手动注册的 bean
81 this.reader.loadBeanDefinitions(configClasses);
82 alreadyParsed.addAll(configClasses);
83
84 candidates.clear();
85 if (registry.getBeanDefinitionCount() > candidateNames.length) {
86 String[] newCandidateNames = registry.getBeanDefinitionNames();
87 Set oldCandidateNames = new HashSet(Arrays.asList(candidateNames));
88 Set alreadyParsedClasses = new HashSet();
89 for (ConfigurationClass configurationClass : alreadyParsed) {
90 alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
91 }
92 for (String candidateName : newCandidateNames) {
93 if (!oldCandidateNames.contains(candidateName)) {
94 BeanDefinition bd = registry.getBeanDefinition(candidateName);
95 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
96 !alreadyParsedClasses.contains(bd.getBeanClassName())) {
97 candidates.add(new BeanDefinitionHolder(bd, candidateName));
98 }
99 }
100 }
101 candidateNames = newCandidateNames;
102 }
103 }
104 while (!candidates.isEmpty());
105
106 // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
107 if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
108 sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
109 }
110
111 ......
112 }
3. org.springframework.context.annotation.ConfigurationClassParser#parse(Set configCandidates) // 最先处理 PlainApplication,它本身就是一个 @Configuration class
3.1 org.springframework.context.annotation.ConfigurationClassParser#parse(..., String beanName)
3.1.1 org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass() // 处理 @PropertySource -> @ComponentScan -> @Import -> @ImportResource -> @Bean -> 处理 Java 8+ ConfigurationClass 实现的接口上的 @Bean -> 处理 supper class
3.1.1.1 org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition // 注册 @ComponentScan 扫描出来的 bean,即 PlainApplication 所在的包(com.kvn.boot)及子包下的 bean
3.2 org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorHandler#process() // 迟延导入选择器处理,导入通过 PlainApplication 扫描出来的 @Configuration Class
3.2.1 org.springframework.context.annotation.ConfigurationClassParser.DeferredImportSelectorGroupingHandler#processGroupImports()
3.2.1.1 处理 ImportSelector
3.2.1.2 处理 ImportBeanDefinitionRegistrar
3.2.1.3 其他情况,按照 @Configuration 来处理
3.2.1.3.1 org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass // 回到 3.1.1 的处理
3. org.springframework.context.annotation.ConfigurationClassParser#parse(Set configCandidates)
1 public void parse(Set configCandidates) {
2 for (BeanDefinitionHolder holder : configCandidates) {
3 BeanDefinition bd = holder.getBeanDefinition();
4 try {
5 if (bd instanceof AnnotatedBeanDefinition) {
6 parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
7 }
8 else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
9 parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
10 }
11 else {
12 parse(bd.getBeanClassName(), holder.getBeanName());
13 }
14 }
15 catch (BeanDefinitionStoreException ex) {
16 throw ex;
17 }
18 catch (Throwable ex) {
19 throw new BeanDefinitionStoreException(
20 "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
21 }
22 }
23
24 // 迟延导入选择器处理程序,导入 PlainApplication 扫描出来的 Configuration Class
25 this.deferredImportSelectorHandler.process();
26 }
View Code
3.1.1 org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass()
1 protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
2 throws IOException {
3
4 if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
5 // Recursively process any member (nested) classes first
6 processMemberClasses(configClass, sourceClass);
7 }
8
9 // Process any @PropertySource annotations
10 // 处理 @PropertySource
11 for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
12 sourceClass.getMetadata(), PropertySources.class,
13 org.springframework.context.annotation.PropertySource.class)) {
14 if (this.environment instanceof ConfigurableEnvironment) {
15 processPropertySource(propertySource);
16 }
17 else {
18 logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
19 "]. Reason: Environment must implement ConfigurableEnvironment");
20 }
21 }
22
23 // Process any @ComponentScan annotations
24 // 处理 @ComponentScan。@ComponentScan 扫描出来的 bean 会优先注册到容器中。
25 // 此时,扫描出来的 bean 如果使用条件加载的方式(即:@ConditionalOnXxx)进行注册,则有可能达不到预期。(原因见上面)
26 Set componentScans = AnnotationConfigUtils.attributesForRepeatable(
27 sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
28 if (!componentScans.isEmpty() &&
29 !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
30 for (AnnotationAttributes componentScan : componentScans) {
31 // The config class is annotated with @ComponentScan -> perform the scan immediately
32 // 立马执行 scan 扫描
33 Set scannedBeanDefinitions =
34 this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
35 // Check the set of scanned definitions for any further config classes and parse recursively if needed
36 for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
37 BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
38 if (bdCand == null) {
39 bdCand = holder.getBeanDefinition();
40 }
41 if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
42 // @ComponentScan 扫描出来的 bean 最先注册到容器中
43 parse(bdCand.getBeanClassName(), holder.getBeanName());
44 }
45 }
46 }
47 }
48
49 // Process any @Import annotations
50 // 处理 @Import
51 processImports(configClass, sourceClass, getImports(sourceClass), true);
52
53 // Process any @ImportResource annotations
54 // 处理 @ImportResource
55 AnnotationAttributes importResource =
56 AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
57 if (importResource != null) {
58 String[] resources = importResource.getStringArray("locations");
59 Class extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
60 for (String resource : resources) {
61 String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
62 configClass.addImportedResource(resolvedResource, readerClass);
63 }
64 }
65
66 // Process individual @Bean methods
67 // 处理 @Bean
68 Set beanMethods = retrieveBeanMethodMetadata(sourceClass);
69 for (MethodMetadata methodMetadata : beanMethods) {
70 configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
71 }
72
73 // Process default methods on interfaces
74 // 处理接口中的 @Bean 方法(Java 8+ 支持接口使用默认实现方法)
75 processInterfaces(configClass, sourceClass);
76
77 // Process superclass, if any
78 // 处理父类
79 if (sourceClass.getMetadata().hasSuperClass()) {
80 String superclass = sourceClass.getMetadata().getSuperClassName();
81 if (superclass != null && !superclass.startsWith("java") &&
82 !this.knownSuperclasses.containsKey(superclass)) {
83 this.knownSuperclasses.put(superclass, configClass);
84 // Superclass found, return its annotation metadata and recurse
85 return sourceClass.getSuperClass();
86 }
87 }
88
89 // No superclass -> processing is complete
90 return null;
91 }
4. org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions()
4.1 org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass()
1 private void loadBeanDefinitionsForConfigurationClass(
2 ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
3
4 // a. 判断是否要 skip
5 if (trackedConditionEvaluator.shouldSkip(configClass)) {
6 String beanName = configClass.getBeanName();
7 if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
8 this.registry.removeBeanDefinition(beanName);
9 }
10 this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
11 return;
12 }
13
14 // b. 处理 import 导入的 @Configuration 类,将它注册到容器中。beanName 默认为类的全限定名,如:com.kvn.other.Config1
15 if (configClass.isImported()) {
16 registerBeanDefinitionForImportedConfigurationClass(configClass);
17 }
18 // c. 将 ConfigurationClass 中的 BeanMethod 类型的 bean(即 @Bean 标记的方法)注册到容器中
19 for (BeanMethod beanMethod : configClass.getBeanMethods()) {
20 loadBeanDefinitionsForBeanMethod(beanMethod);
21 }
22
23 // d. 将 @ImportResources 中的 bean 注册到容器中
24 loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
25 // e. 注册 ImportBeanDefinitionRegistrar 接口中手动注册的 bean
26 loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
27 }
附:
// bean name 生成规则
1. org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan()
1.1 org.springframework.beans.factory.support.BeanNameGenerator#generateBeanName()
SpringBoot Bean 扫描注册核心之:ConfigurationClassPostProcessor 详解
标签:metadata parser register 如何 类型 ann property mes config
原文地址:https://www.cnblogs.com/kevin-yuan/p/12716050.html