欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Spring5源码解析5-ConfigurationClassPostProcessor (上)

程序员文章站 2023-11-23 00:02:28
接上回,我们讲到了`refresh()`方法中的`invokeBeanFactoryPostProcessors(beanFactory)`方法主要在执行`BeanFactoryPostProcessor`和其子接口`BeanDefinitionRegistryPostProcessor`的方法。 ......

接上回,我们讲到了refresh()方法中的invokebeanfactorypostprocessors(beanfactory)方法主要在执行beanfactorypostprocessor和其子接口beandefinitionregistrypostprocessor的方法。

在创建annotationconfigapplicationcontext对象时spring就添加了一个非常重要的beanfactorypostprocessor接口实现类:configurationclasspostprocessor。注意,这里说的添加只是添加到容器的beandefinitionmap中,还没有创建真正的实例bean。

简单回顾一下configurationclasspostprocessor是在什么时候被添加到容器中的:在annotationconfigapplicationcontext的无参构造器中创建annotatedbeandefinitionreader对象时会向传入的beandefinitionregistry中注册解析注解配置类相关的processors的beandefinitionconfigurationclasspostprocessor就是在此处被添加到容器中的。


configurationclasspostprocessor

先看一些configurationclasspostprocessor的继承体系:

Spring5源码解析5-ConfigurationClassPostProcessor (上)

configurationclasspostprocessor实现了beandefinitionregistrypostprocessor接口,也就拥有了在spring容器启动时,往容器中注册beandefinition的能力。

我们知道,configurationclasspostprocessor#postprocessbeandefinitionregistry方法是在refresh();方法中的invokebeanfactorypostprocessors(beanfactory);中被执行的,下面我们就一起来看一下该方法。

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);
}

主要的逻辑在processconfigbeandefinitions(registry);中,点开源码:

public void processconfigbeandefinitions(beandefinitionregistry registry) {
    list<beandefinitionholder> configcandidates = new arraylist<>();
    //获取所有的beandefinitionname
    string[] candidatenames = registry.getbeandefinitionnames();

    for (string beanname : candidatenames) {
        beandefinition beandef = registry.getbeandefinition(beanname);

        // https://docs.spring.io/spring/docs/5.1.8.release/spring-framework-reference/core.html#beans-java-basic-concepts
        // full @configuration vs “lite” @bean mode
        if (configurationclassutils.isfullconfigurationclass(beandef) ||
                configurationclassutils.isliteconfigurationclass(beandef)) {
            if (logger.isdebugenabled()) {
                logger.debug("bean definition has already been processed as a configuration class: " + beandef);
            }
        }

        // 校验是否为配置类
        // 配置类分为两种 full @configuration vs “lite” @bean mode
        // 校验之后在 beandefinition 中添加标志属性
        // 如果满足条件则加入到configcandidates
        else if (configurationclassutils.checkconfigurationclasscandidate(beandef, this.metadatareaderfactory)) {
            // 如果是配置类,就放到 configcandidates 变量中
            configcandidates.add(new beandefinitionholder(beandef, beanname));
        }
    }

    // return immediately if no @configuration classes were found
    if (configcandidates.isempty()) {
        return;
    }

    // sort by previously determined @order value, if applicable
    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
    singletonbeanregistry sbr = null;
    // 传入的 registry 是 defaultlistablebeanfactory
    if (registry instanceof singletonbeanregistry) {
        sbr = (singletonbeanregistry) registry;
        if (!this.localbeannamegeneratorset) {
            //获取自定义beannamegenerator,一般情况下为空
            beannamegenerator generator = (beannamegenerator) sbr.getsingleton(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
    // new configurationclassparser,用来解析 @configuration 类
    configurationclassparser parser = new configurationclassparser(
            this.metadatareaderfactory, this.problemreporter, this.environment,
            this.resourceloader, this.componentscanbeannamegenerator, registry);

    // 将 configcandidates 转成 set  candidates , 去重
    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());
        }
        // import类,@bean,@importresource 转化为 beandefinition
        this.reader.loadbeandefinitions(configclasses);
        alreadyparsed.addall(configclasses);

        candidates.clear();
        // 再获取一下容器中beandefinition的数据,如果发现数量增加了,说明有新的beandefinition被注册了
        if (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 classes
    if (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();
    }
}

获取所有的beandefinitionnames,然后循环这个数组,判断其是否为配置类。

Spring5源码解析5-ConfigurationClassPostProcessor (上)

前5个是spring注册的内置processor,最后一个是传入给annotationconfigapplicationcontext的配置类appconfig.class

在spring中存在两种configurationclass,一种是fullconfigurationclass另一种是liteconfigurationclass。关于这两者的区别,可以参看笔者文章之前关于full @configuration 和 lite @bean mode的文章。

configurationclassutils#checkconfigurationclasscandidate方法内部就是在判断属于哪种配置类,并在beandefinition中标记判断结果。其具体的判断逻辑如下:

public static boolean isfullconfigurationcandidate(annotationmetadata metadata) {
    return metadata.isannotated(configuration.class.getname());
}
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 isliteconfigurationcandidate(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;
    }
}

判断configcandidates变量中存放的

配置类是否为空,如果不为空,则对其进行排序。

Spring5源码解析5-ConfigurationClassPostProcessor (上)

创建configurationclassparser对象,用于解析@configuration类,完成包的扫描、beandefinition的注册。主要通过执行parser.parse(candidates);方法来完成。

执行parser.parse(candidates)方法前 :

Spring5源码解析5-ConfigurationClassPostProcessor (上)

执行parser.parse(candidates)方法后 :

Spring5源码解析5-ConfigurationClassPostProcessor (上)

解析完配置类之后,紧接着又执行了this.reader.loadbeandefinitions(configclasses);方法。这个方法主要是用来处理import类@bean@importresource注解。关于这两个方法的具体细节,我们下次再讲。

最后又加了入了对importaware接口支持所需要的bean。

// register the importregistry as a bean in order to support importaware @configuration classes
if (sbr != null && !sbr.containssingleton(import_registry_bean_name)) {
    sbr.registersingleton(import_registry_bean_name, parser.getimportregistry());
}

关于对importaware接口的使用,我们也下次再讲。


未完待续......

源码学习笔记:https://github.com/shenjianeng/spring-code-study

欢迎各位关注公众号,大家一起学习成长。
Spring5源码解析5-ConfigurationClassPostProcessor (上)