SpringConfiguration注解

编程

Spring @Configuration标识的class,能够作为Spring的配置类,用来构建Bean,引入资源文件等等功能。所以有必要理解一下它是如何工作的。

注册 ConfigurationClassPostProcessor Bean
此注解的核心处理逻辑在ConfigurationClassPostProcessor类中,此类实现了BeanDefinitionRegistryPostProcessor接口,所以此类也必须被注册成Bean。ConfigurationClassPostProcessor注册的逻辑在AnnotationConfigUtils.registerAnnotationConfigProcessors()方法内

...
public static final String CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME =
        "org.springframework.context.annotation.internalConfigurationAnnotationProcessor";
...
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
    RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
    def.setSource(source);
    beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
...
1
2
3
4
5
6
7
8
9
10
上面的注册ConfigurationClassPostProcessor会在AnnotatedBeanDefinitionReader构造时调用。

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
    ...
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
1
2
3
4
AnnotatedBeanDefinitionReader类是用来处理注解方式注册bean的处理器。此类会在AnnotationConfigEmbeddedWebApplicationContext或者AnnotationConfigApplicationContext构造时构建。对于前2者context,SpringBoot会在启动时,会根据类路径下是否有web相关的类,决定启用哪个context。

到这里为止,ConfigurationClassPostProcessor注册为bean的流程已经显而易见了。

ConfigurationClassPostProcessor 处理 @Configuration 注解
由于ConfigurationClassPostProcessor继承了BeanDefinitionRegistryPostProcessor接口,直接看它实现的postProcessBeanDefinitionRegistry方法,发现它的核心处理逻辑在processConfigBeanDefinitions方法内。

我们将它的内部流程分为几块来看。

判断是否有 @Configuration 注解标注
String[] candidateNames = registry.getBeanDefinitionNames();

for (String beanName : candidateNames) {
    BeanDefinition beanDef = registry.getBeanDefinition(beanName);
    if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
            ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
        if (logger.isDebugEnabled()) {
            logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
        }
    }
    else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
        configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
    }
}

// Return immediately if no @Configuration classes were found
if (configCandidates.isEmpty()) {
    return;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
上面这段代码里,首先isFullConfigurationClass和isLiteConfigurationClass方法判断此类是否已经经过处理,如果不是,调用ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory),判断此类是否标有@Configuration注解,如果有的话放入configCandidates;

checkConfigurationClassCandidate方法

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
    String className = beanDef.getBeanClassName();
    if (className == null || beanDef.getFactoryMethodName() != null) {
        return false;
    }

    AnnotationMetadata metadata;
    ...
    if (isFullConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    else if (isLiteConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
        return false;
    }
    // It"s a full or lite configuration candidate... Let"s determine the order value, if any.
    Map<String, Object> orderAttributes = metadata.getAnnotationAttributes(Order.class.getName());
    if (orderAttributes != null) {
        beanDef.setAttribute(ORDER_ATTRIBUTE, orderAttributes.get(AnnotationUtils.VALUE));
    }
    return true;
}

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
    return metadata.isAnnotated(Configuration.class.getName());
}

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
...
    // 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());
    }
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
isFullConfigurationCandidate判断是否标有Configuration注解;
isLiteConfigurationCandidate判断是否有Component,ComponentScan,Import,ImportResource,Bean注解,但是没有Configuration注解。
checkConfigurationClassCandidate方法内,会根据不同的配置类类型,对BeanDefinition设置不同的属性,full or lite
处理标有 Configuration 注解的类
ConfigurationClassPostProcessor类的processConfigBeanDefinitions方法里剩下的代码

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
do {
    parser.parse(candidates);
    parser.validate();

    Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(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);
    alreadyParsed.addAll(configClasses);

    candidates.clear();
    if (registry.getBeanDefinitionCount() > candidateNames.length) {
        String[] newCandidateNames = registry.getBeanDefinitionNames();
        Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
        Set<String> alreadyParsedClasses = new HashSet<String>();
        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());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
此处可以看到parser调用了parse()方法,此方法内会解析配置类.内部比较核心的方法是doProcessConfigurationClass

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
        throws IOException {
    ...
    // Process any @PropertySource annotations
    // 处理 @PropertySources
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
            sourceClass.getMetadata(), PropertySources.class,
            org.springframework.context.annotation.PropertySource.class)) {
        if (this.environment instanceof ConfigurableEnvironment) {
            processPropertySource(propertySource);
        }
        else {
            logger.warn("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 immediately
            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
            for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(
                        holder.getBeanDefinition(), this.metadataReaderFactory)) {
                    parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
                }
            }
        }
    }

    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), true);

    // Process any @ImportResource annotations
    if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) {
        AnnotationAttributes importResource =
                AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        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
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
从上面的代码可以看到此方法内处理了各种常用的注解。具体就不在此文里深入,对于其它注解之后会在专门的文章里描述。

回到parser.parse调用之后的逻辑,Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());调用parser.getConfigurationClasses()获取到配置类需要注册为bean的所有类。

if (this.reader == null) {
    this.reader = new ConfigurationClassBeanDefinitionReader(
            registry, this.sourceExtractor, this.resourceLoader, this.environment,
            this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);
1
2
3
4
5
6
上面这段代码会将获取到的所有类,通过ConfigurationClassBeanDefinitionReader.loadBeanDefinitions方法,注册为bean;

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
        TrackedConditionEvaluator trackedConditionEvaluator) {

    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
        return;
    }

    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
到这里为止,@Configuration配置类解析,注册相关的bean的流程已经比较清晰了。具体细节还是不再深入。

总结
@Configuration实现过程主要基于BeanDefinitionRegistryPostProcessor接口,解析注解、处理资源等,最后动态注册bean来实现。不得不说Spring内部的几个XxxPostProcessor对于扩展非常方便。
————————————————
版权声明:本文为CSDN博主「_laomei_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sweatOtt/article/details/83154808

以上是 SpringConfiguration注解 的全部内容, 来源链接: utcz.com/z/514728.html

回到顶部