Spring配置类解析(上)

导读:本篇文章讲解 Spring配置类解析(上),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

配置类解析

《Spring容器启动(下)》中说到了,Spring启动过程中最核心的方法就是invokeBeanFactoryPostProcessors(),该方法会按照优先级去调用实现了BeanDefinitionRegistryPostProcessor接口和BeanFactoryPostProcessor接口的postProcessBeanDefinitionRegistry()和postProcessBeanFactory(),而这两个方法中,实现了配置类的扫描解析与增强,这篇文章将重点介绍配置类的扫描过程。

PostProcessorRegistrationDelegate类的invokeBeanFactoryPostProcessors()方法中,核心的两个方法是invokeBeanDefinitionRegistryPostProcessors()和invokeBeanFactoryPostProcessors(),这两个方法的源码如下

/**
 * Invoke the given BeanDefinitionRegistryPostProcessor beans.
 */
private static void invokeBeanDefinitionRegistryPostProcessors(
      Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) {

   for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
      StartupStep postProcessBeanDefRegistry = applicationStartup.start("spring.context.beandef-registry.post-process")
            .tag("postProcessor", postProcessor::toString);
      postProcessor.postProcessBeanDefinitionRegistry(registry);
      postProcessBeanDefRegistry.end();
   }
}
private static void invokeBeanFactoryPostProcessors(
      Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {

   for (BeanFactoryPostProcessor postProcessor : postProcessors) {
      StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
            .tag("postProcessor", postProcessor::toString);
      postProcessor.postProcessBeanFactory(beanFactory);
      postProcessBeanFactory.end();
   }
}

Spring中主要的解析配置类的方法是ConfigurationClassPostProcessor类的postProcessBeanDefinitionRegistry(),而整个解析配置的核心逻辑在processConfigBeanDefinitions()中

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
   int registryId = System.identityHashCode(registry);
   ……
   this.registriesPostProcessed.add(registryId);

   // 解析配置类
   processConfigBeanDefinitions(registry);
}

一、判断配置类

在解析配置类之前,首先需要判断是否该类是否是配置类,只有配置类才需要进行解析

首先获取当前容器中已经添加的BeanDefinitonName,由于此时还没有进行扫描,此时容器中的BeanDefinition只有容器启动时手动注册的配置类的BeanDefinition和Spring自己添加的一些常用的BeanPostProcessor的BeanDefinition,这些BeanDefinition在《Spring容器启动(上)》中有作说明

获取到这些BeanDefiniton后,会去判断是否是配置类,如果是配置类,就添加到configCandidates中,后续遍历解析

ConfigurationClassUtils的checkConfigurationClassCandidate()用于判断是否为配置类

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();

    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        // 什么是配置类?
        else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    ……
}

1.1 @Bean对应的类不能为配置类

假设有一个类A,满足属于配置类的条件,但是在类B中,通过@Bean定义了A的实例,那么类A将不能作为配置类处理,通过@Bean生成BeanDefiniton,它的beanClassName为null,或者factoryMethodName不为null

@configuration
Class A{
}

Class B{
    @Bean
    public A a(){
        return new A();
    }
}
public static boolean checkConfigurationClassCandidate(
    BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

    // @Bean定义的配置类Bean是不起作用的 
    String className = beanDef.getBeanClassName();
    if (className == null || beanDef.getFactoryMethodName() != null) {
        return false;
    }
	……
}

1.2 获取注解信息

获取该类的所有注解信息,后面会根据注解来判断该类是否为配置类

// AnnotationMetadata表示某个类的注解信息,但是并一定要加载这个类
AnnotationMetadata metadata;
//省略获取类注解信息的代码,会通过三种判断来获取注解信息
……
    MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
metadata = metadataReader.getAnnotationMetadata();

1.3 判断@Configuration注解

如果一个类上加了@Configuration注解,它就是一个配置类,但该注解的proxyBeanMethods属性又将配置类分为Full和lite配置类,具体后面再介绍

Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());

// 存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类
if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
// 存在@Configuration,并且proxyBeanMethods为false时,是lite配置类
// 或者不存在@Configuration,但是只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类
// 或者不存在@Configuration,只要存在@Bean注解了的方法,就是lite配置类
else if (config != null || isConfigurationCandidate(metadata)) {
    beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
    return false;
}

如果该类没有添加@Configuration注解,则通过isConfigurationCandidate()判断是否为配置类

通过这种方式判断出来的配置类都属于lite类型

如果是一个接口,则不属于配置类;如果有Component、ComponentScan、Import、ImportResource中的任一个注解,就是配置类;如果有@Bean定义的方法,就是一个配置类

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...
    return hasBeanMethods(metadata);
}

static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
}

1.4 设置Order值

判断完是否为配置后,设置BeanDifinition的Order值,用于对配置类进行排序,按优先级进行解析


// 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;

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/112153.html

(0)
Java光头强的头像Java光头强

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!