Spring之基于Java类的配置bean的加载注册流程

描述

Spring Bean定义的三种方式:

  1. 基于XML的配置
  2. 基于注解的配置,在类中加入如下注解通过包扫描加载注册bean:
    @Component:当对组件的层次难以定位的时候使用这个注解
    @Controller:表示控制层的组件
    @Service:表示业务逻辑层的组件
    @Repository:表示数据访问层的组件
  1. 基于Java类的配置:
  • 使用@Configuration注解需要作为配置的类,表示该类将定义Bean的元数据
  • 使用@Bean注解相应的方法,该方法名默认就是Bean的名称,该方法返回值就是Bean的对象。

下面跟踪源码查看spring基于Java类的配置定义bean的加载注册流程

Java类配置的模型

  • @Configuration描述的类模型ConfigurationClass
 final class ConfigurationClass {

    /**
     * 注解元数据
     */
    private final AnnotationMetadata metadata;

    /**
     * 资源信息
     */
    private final Resource resource;

    /**
     * bean定义的名称
     */
    @Nullable
    private String beanName;

    /**
     * import 注解导入的配置类(被@Configuration注解的类)集合
     */
    private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);

    /**
     * 配置类下被@Bean注解的方法结合
     */
    private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();

    /**
     * 跳过的方法名集合
     */
    final Set<String> skippedBeanMethods = new HashSet<>();
}   

ConfigurationClass 类主要用于描述被@Configuration注解的配置类的信息,我们在spring中定义的配置类,会被spring首先解析成BeanDefinition,然后spring会根据此BeanDefinition创建配置类对应的ConfigurationClass

如下类,spring会生成对应的ConfigurationClass模型

@Configuration
@Import({ SessionRouteConfig.class })
@EnableConfigurationProperties({ ServiceInstanceProperties.class, SessionProperties.class })
public class GatewayConfiguration {
    @Bean
    @ConditionalOnMissingBean
    SessionFilterPredicateFactory sessionFilterPredicateFactory() {
        return new DefaultSessionFilterPredicateFactory();
    }

}   
  • 配置类中被@Bean注解的方法模型BeanMethod
final class BeanMethod extends ConfigurationMethod {

    public BeanMethod(MethodMetadata metadata, ConfigurationClass configurationClass) {
        super(metadata, configurationClass);
    }

    @Override
    public void validate(ProblemReporter problemReporter) {
        if (getMetadata().isStatic()) {
            // static @Bean methods have no constraints to validate -> return immediately
            return;
        }

        if (this.configurationClass.getMetadata().isAnnotated(Configuration.class.getName())) {
            if (!getMetadata().isOverridable()) {
                // instance @Bean methods within @Configuration classes must be overridable to accommodate CGLIB
                problemReporter.error(new NonOverridableMethodError());
            }
        }
    }
}   

BeanMethod 用于描述配置类下被@Bean修饰的方法的模型,spring会BeanMethod 模型创建方法返回类的BeanDefinition,并将其注册到bean定义注册中心,以达到bean定义的加载注册,如上例中sessionFilterPredicateFactory方法,spring会生成对应的BeanMethod 模型与之对应。

java配置定义bean加载解析入口

// 类 ConfigurationClassPostProcessor 
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
        PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {
    /**
     * 从注册表中的配置类派生进一步的bean定义。
     * Derive further bean definitions from the configuration classes in the registry.
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        //处理配置类的bean定义
        processConfigBeanDefinitions(registry);
    }
    /**
     * Build and validate a configuration model based on the registry of
     * {@link Configuration} classes.
     */
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        //存在配置类bean定义的集合,用于存储所有被@Configuration注解的类
        List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
        //获取bean定义注册中心的bean定义名称集合
        String[] candidateNames = registry.getBeanDefinitionNames();

        for (String beanName : candidateNames) {
            //遍历名称根据名称在注册中心获取当前名称的BeanDefinition
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);

            //校验当前bean定义是否为配置类
            //判断当前的bean定义是否为存在精简或者完整的配置类标识,当存在此标识时标识已被处理过
            if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                    ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
                //检测bean定义是否已经被处理
                if (logger.isDebugEnabled()) {
                    logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
                }
            }
            //检测给定的bean定义是否是描述的配置类
            else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                //将配置类的bean定义添加到configCandidates
                configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
        }

        //配置类bean定义的集合为空时不做任何处理直接返回
        // Return immediately if no @Configuration classes were found
        if (configCandidates.isEmpty()) {
            return;
        }

        //配置类bean定义的集合 排序
        // 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;
        if (registry instanceof SingletonBeanRegistry) {
            sbr = (SingletonBeanRegistry) registry;
            if (!this.localBeanNameGeneratorSet) {
                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();
        }

        //创建配置类的解析器,解析每个@Configuration注解的类
        // Parse each @Configuration class
        ConfigurationClassParser parser = new ConfigurationClassParser(
                this.metadataReaderFactory, this.problemReporter, this.environment,
                this.resourceLoader, this.componentScanBeanNameGenerator, registry);

        //准备解析的bean定义集合
        Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
        //处理完成的模型的集合
        Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
        do {
            //解析bean定义,生成ConfigurationClass模型
            parser.parse(candidates);
            //校验ConfigurationClass模型
            parser.validate();

            //获取解析到的ConfigurationClass模型集合
            Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
            //当前处理的模型中移除已经处理的的模型
            configClasses.removeAll(alreadyParsed);

            //读取模型并根据内容创建bean定义
            // 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());
            }
            //加载创建模型中的所有bean定义
            this.reader.loadBeanDefinitions(configClasses);
            //当前模型集合添加到已处理集合中
            alreadyParsed.addAll(configClasses);

            candidates.clear();
            //判断注册中心中注册表是否存在新增数据,如果有新增数据,重新遍历获取新增数据解析
            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());
    }

}

类 ConfigurationClassPostProcessor 为spring对@Configuration解析处理的后置处理器。

  • ConfigurationClassPostProcessor实现了BeanDefinitionRegistryPostProcessor接口,此接口是BeanFactoryPostProcessor的扩展接口,在BeanFactoryPostProcessor执行流程笔记中分析过,允许在正常的BeanFactoryPostProcessor执行检测开始之前注册更多的自定义bean。也就是说BeanDefinitionRegistryPostProcessor的方法postProcessBeanDefinitionRegistry可以在后置处理器执行前自定义注册更多的BeanDefinition

ConfigurationClassPostProcessor 的postProcessBeanDefinitionRegistry方法完成的功能如下:

  1. 遍历bean定义注册器中所有的bean定义查找配置bean定义(元数据中含有@Configuration注解的bean定义)
  2. 通过配置类解析器ConfigurationClassParser 根据配置bean定义创建配置模型ConfigurationClass和BeanMethod
    //解析bean定义,生成ConfigurationClass模型
    parser.parse(candidates);
    //校验ConfigurationClass模型
    parser.validate();
  1. 根据配置类模型转换器ConfigurationClassBeanDefinitionReader根据配置类模型创建配置类中的BeanDefinition并注册到BeanDefinition注册中心
    //读取模型并根据内容创建bean定义
    // 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());
    }
    //加载创建模型中的所有bean定义
    this.reader.loadBeanDefinitions(configClasses);

配置类解析器ConfigurationClassParser

配置类解析器ConfigurationClassParser 根据配置类的BeanDefinition生成配置类模型数据ConfigurationClass与BeanMethod

解析主方法parse

    public void parse(Set<BeanDefinitionHolder> configCandidates) {
        this.deferredImportSelectors = new LinkedList<>();

        for (BeanDefinitionHolder holder : configCandidates) {
            //循环遍历配置类的bean定义,解析生成对应的ConfigurationClass模型
            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);
            }
        }

        processDeferredImportSelectors();
    }

此方法最终调用doProcessConfigurationClass方法对ConfigurationClass创建初始化

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
            throws IOException {

        // 递归处理配置类的成员嵌套类
        processMemberClasses(configClass, sourceClass);

        //处理配置类上的@PropertySource注解
        for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
                sourceClass.getMetadata(), PropertySources.class,
                org.springframework.context.annotation.PropertySource.class)) {
            if (this.environment instanceof ConfigurableEnvironment) {
                processPropertySource(propertySource);
            }
            else {
            
            }
        }
        //处理配置类上的@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) {
                    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }
                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                        parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }
         //处理配置类上的@Import注解
        processImports(configClass, sourceClass, getImports(sourceClass), true);
         //处理配置类上的@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);
            }
        }
         //处理配置类里@Bean注解的方法
        Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
        for (MethodMetadata methodMetadata : beanMethods) {
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

        // Process default methods on interfaces
        processInterfaces(configClass, sourceClass);

        // Process superclass, if any
        if (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 recurse
                return sourceClass.getSuperClass();
            }
        }
        return null;
    }

doProcessConfigurationClass 方法依次完成如下功能:

  • 递归处理配置类的成员嵌套类
  • 处理配置类上的@PropertySource注解
  • 处理配置类上的@ComponentScan注解
  • 处理配置类上的@Import注解
  • 处理配置类上的@ImportResource注解
  • 处理配置类里@Bean注解的方法
  • 处理接口上的默认方法
  • 处理配置类的父类,如果有的话

最后spring 通过ConfigurationClassParser类将一个配置类的bean定义,解析成完整的ConfigurationClass(配置数据模型)

配置类模型转换bean定义

ConfigurationClassBeanDefinitionReader 会根据配置类模型ConfigurationClass读取创建BeanDefinition,并将BeanDefinition注册到bean定义注册表中。

加载配置中的BeanDefinition入口loadBeanDefinitions方法:

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
        TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
        for (ConfigurationClass configClass : configurationModel) {
        //遍历ConfigurationClass依次加载注册配置的bean定义
            loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
        }
    }

loadBeanDefinitions 通过调用loadBeanDefinitionsForConfigurationClass加载注册bean定义

private void loadBeanDefinitionsForConfigurationClass(
            ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
        if (configClass.isImported()) {
            //将ConfigurationClass本身的bean定义注册到注册中心
            registerBeanDefinitionForImportedConfigurationClass(configClass);
        }
        for (BeanMethod beanMethod : configClass.getBeanMethods()) {
            // 遍历BeanMethod
            //根据BeanMethod 创建bean定义,此方法根据配置类中被@Bean注解的方法生成bean定义
            //将生成的bean定义注册到注册中心
            loadBeanDefinitionsForBeanMethod(beanMethod);
        }

        //加载来自ImportedResources的bean定义并注册
        loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
        //加载来自Import的bean定义并注册
        loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }

loadBeanDefinitionsForConfigurationClass 根据ConfigurationClass 创建其配置的bean定义,并将其注册到bean定义注册中心


接下来整体梳理下@Configuration配置的bean定义加载解析流程:

  1. 在spring完成基于注解配置bean定义的扫描注册后,通过后置处理器,获取到注册表中所有的bean定义。
  2. 遍历bean定义生成含有@Configuration注解的bean定义的集合。
  3. 根据配置类bean定义集合通过配置类解析器ConfigurationClassParser生成ConfigurationClass集合。
  4. 根据ConfigurationClass集合通过ConfigurationClassBeanDefinitionReader类加载注册配置里的bean定义。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容