面试官:Spring如何解析配置类

你好,我是柳岸花开。

大家好,今天我们来深入探讨一下Spring框架中的配置类解析与扫描过程的源码。Spring作为Java开发中最为广泛使用的框架之一,其核心机制一直是开发者关注的焦点。本文将带领大家从源码角度,详细剖析Spring配置类的解析与扫描过程。

配置类解析概述

在Spring中,配置类主要是通过注解的方式进行定义和解析的。其中,@Configuration@ComponentScan是两个最为重要的注解。

  • @Configuration:标识一个类为配置类,类似于传统的XML配置文件。
  • @ComponentScan:用于指定Spring在初始化时需要扫描的包路径,从而找到标注了特定注解的类。

这些注解的解析过程是通过Spring的内部机制来实现的,下面我们将一步步解析这个过程。

源码分析

1. @Configuration注解的解析

Spring在启动时,会通过ConfigurationClassPostProcessor类对所有标注了@Configuration注解的类进行处理。其核心方法是processConfigBeanDefinitions,该方法的主要工作是扫描所有的bean定义,并找出配置类进行处理。

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

    for (String beanName : candidateNames) {
        BeanDefinition beanDef = registry.getBeanDefinition(beanName);
        if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {
            configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
        }
    }
    // 后续处理逻辑
}

在这个过程中,Spring会识别所有的配置类,并进行后续的处理。

2. @ComponentScan注解的解析

配置类被识别后,Spring会继续解析其中的@ComponentScan注解。这个注解的解析逻辑在ClassPathBeanDefinitionScanner类中实现。其核心方法是doScan,该方法会扫描指定包路径下的所有类,并将符合条件的类注册为Bean。

public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    for (String basePackage : basePackages) {
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        for (BeanDefinition candidate : candidates) {
            beanDefinitions.add(new BeanDefinitionHolder(candidate, generateBeanName(candidate)));
        }
    }
    return beanDefinitions;
}

该方法会递归扫描指定的包路径,并筛选出符合条件的类,注册为Spring的Bean。

解析配置具体步骤

alt
  1. 在启动Spring时,需要传入一个AppConfig.class给ApplicationContext,ApplicationContext会根据AppConfig类封装为一个BeanDefinition,这种BeanDefinition我们把它称为配置类BeanDefinition。
  2. ConfigurationClassPostProcessor中会把配置类BeanDefinition取出来
  3. 构造一个ConfigurationClassParser用来解析配置类BeanDefinition,并且会生成一个配置类对象ConfigurationClass
  4. 如果配置类上存在@Component注解,那么 解析配置类中的内部类(这里有递归,如果内部类也是配置类的话)
  5. 如果配置类上存在@PropertySource注解,那么则解析该注解,并得到PropertySource对象,并添加到environment中去
  6. 如果配置类上存在@ComponentScan注解,那么则解析该注解,进行扫描,扫描得到一系列的BeanDefinition对象,然后判断这些BeanDefinition是不是也是配置类BeanDefinition(只要存在@Component注解就是配置类,所以基本上扫描出来的都是配置类),如果是则继续解析该配置类, (也有递归),并且会生成对应的ConfigurationClass
  7. 如果配置类上存在@Import注解,那么则判断Import的类的类型:
    1. 如果是ImportSelector,那么调用执行selectImports方法得到类名,然后在把这个类当做配置类进行解析 (也是递归)
    2. 如果是ImportBeanDefinitionRegistrar,那么则生成一个ImportBeanDefinitionRegistrar实例对象,并添加到配置类对象中(ConfigurationClass)的 importBeanDefinitionRegistrars属性中。
  8. 如果配置类上存在@ImportResource注解,那么则把导入进来的资源路径存在配置类对象中的 importedResources属性中。
  9. 如果配置类中存在@Bean的方法,那么则把这些方法封装为BeanMethod对象,并添加到配置类对象中的 beanMethods属性中。
  10. 如果配置类实现了某些接口,则看这些接口内是否定义了@Bean的默认方法
  11. 如果配置类有父类,则把父类当做配置类进行解析
  12. AppConfig这个配置类会对应一个ConfigurationClass,同时在解析的过程中也会生成另外的一些ConfigurationClass,接下来就利用reader来进一步解析ConfigurationClass
    1. 如果ConfigurationClass是通过@Import注解导入进来的,则把这个类生成一个BeanDefinition,同时解析这个类上@Scope,@Lazy等注解信息,并注册BeanDefinition
    2. 如果ConfigurationClass中存在一些BeanMethod,也就是定义了一些@Bean,那么则解析这些@Bean,并生成对应的BeanDefinition,并注册
    3. 如果ConfigurationClass中导入了一些资源文件,比如xx.xml,那么则解析这些xx.xml文件,得到并注册BeanDefinition
    4. 如果ConfigurationClass中导入了一些ImportBeanDefinitionRegistrar,那么则执行对应的registerBeanDefinitions进行BeanDefinition的注册

👇关注我,下期了解👇

Spring之整合Mybatis底层源码解析

alt

回复 222,获取Java面试题合集

关于我

一枚爱折腾的Java程序猿,专注Spring干货。把路上的问题记录下来,帮助那些和我一样的人。

好奇心强,喜欢并深入研究古天文。

崇尚 个人系统创建,做一些时间越长越有价值的事情。思考 把时间留下来 又 每刻都是新的。

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/26005.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度探索Copilot插件

Copilot是一个由GitHub开发的程序代码生成助手&#xff0c;它使用人工智能模型训练来生成代码提示和建议。深度探索Copilot插件意味着探索如何使用和最大限度地利用Copilot来加快和改善编程流程。 首先&#xff0c;你可以在编辑器中安装Copilot插件。它可以与多种编辑器和IDE集…

红黑树/红黑树迭代器封装(C++)

本篇将会较为全面的讲解有关红黑树的特点&#xff0c;插入操作&#xff0c;然后使用代码模拟实现红黑树&#xff0c;同时还会封装出红黑树的迭代器。 在 STL 库中的 set 和 map 都是使用红黑树封装的&#xff0c;在前文中我们讲解了 AVL树&#xff0c;对于红黑树和 AVL 树来说&…

【设计模式】创建型设计模式之 原型模式

介绍 原型模式是一种创建型设计模式&#xff0c;主要用于创建重复的对象&#xff0c;而无需重新初始化它们&#xff0c;从而提高效率并简化对象的创建过程。此模式的核心思想是利用已存在的对象实例&#xff0c;通过复制&#xff08;克隆&#xff09;的方式来生成新的对象&…

k8s 1.28 搭建rabbitmq集群

1.环境 1.1 k8s 1.28 1.2 rabbit 3.8 1.3 工作空间default 1.4 注意&#xff0c;内存最好充足一点&#xff0c;因为我就两个节点一个master、一个node&#xff0c;起初我的node是8g&#xff0c;还剩3~4G&#xff0c;集群竟然一直起不来&#xff0c;后来将虚拟机内存扩大&#x…

Word中插入Mathtype右编号,调整公式与编号的位置

当你已经将mathtype内置于word后&#xff0c;可以使用右编号快速插入公式 但是往往会出现公式和编号出现的位置或之间的距离不合适 比如我在双栏下插入公式&#xff0c;会发现插入的公式与编号是适用于单栏的 解决办法&#xff1a; 开始->样式->MTDisplayLquation -&g…

37python数据分析numpy基础之save以二进制保存数组数据到文件

1 python数据分析numpy基础之save以二进制保存数组数据到文件 python的numpy库的save(file,arr)函数&#xff0c;将数组以二进制格式保存到一个npy后缀的文件中。 用法 numpy.save(file, arr, allow_pickleTrue, fix_importsTrue)描述 numpy.save(file,arr)&#xff0c;可以…

AWT常用组件

AWT中常用组件 前言一、基本组件组件名标签(Label类)Label类的构造方法注意要点 按钮(Button)Button的构造方法注意要点 文本框(TextField)TextField类的构造方法注意要点 文本域&#xff08;TextArea&#xff09;TextArea 的构造方法参数scrollbars的静态常量值 复选框&#x…

【Spring Boot】Spring Boot 的世界之旅1

目录 1 Spring Boot 的诞生背景 2 Spring Boot 的核心价值 3 为什么选择Spring Boot 4 Spring Boot 与传统Spring应用的对比 5 踏上Spring Boot之旅 1 Spring Boot 的诞生背景 在软件开发的历史长河中&#xff0c;随着技术的不断演进&#xff0c;开发者们面临着越来越多的…

Java基础知识:为面试做好准备

基本概念 Java的特性&#xff1a;Java是一门面向对象的编程语言&#xff0c;具有跨平台性、自动内存管理等特点。Java平台的组成&#xff1a;Java平台主要分为Java SE&#xff08;Standard Edition&#xff09;、Java EE&#xff08;Enterprise Edition&#xff09;和Java ME&…

排序-读取数据流并实时返回中位数

目录 一、问题描述 二、解题思路 1.顺序表排序法 2.使用大根堆、小根堆 三、代码实现 1.顺序表排序法实现 2.大根堆、小根堆法实现 四、刷题链接 一、问题描述 二、解题思路 1.顺序表排序法 &#xff08;1&#xff09;每次读取一个数就对列表排一次序&#xff0c;对排…

如何使用Python中的枚举类型(enum)

在Python中&#xff0c;枚举类型可以通过内置的enum模块来实现。枚举类型是一种特殊的类&#xff0c;它用于定义一组命名的常量。这些常量通常用于表示固定的、有限的集合的值&#xff0c;比如一周的几天、颜色的名称等。 下面是如何使用Python中的enum模块来定义和使用枚举类…

AQS实现原理

AQS&#xff08;AbstractQueuedSynchronizer&#xff09;是一个用于构建锁和同步器的框架&#xff0c;许多同步器都可以通过AQS很容易并且高效地构造出来。 不仅 ReentrantLock 和 Semaphore 是基于AQS构建的&#xff0c;还包括 CountDownLatch、ReentrantReadWriteLock、Synch…

速盾:图片cdn加速 免费

随着互联网的快速发展&#xff0c;图片在网页设计和内容传播中起着重要的作用。然而&#xff0c;随着网站访问量的增加和图片文件大小的增加&#xff0c;图片加载速度可能会成为一个问题。为了解决这个问题&#xff0c;许多网站使用图片CDN加速服务。 CDN&#xff08;Content …

Oracle函数有哪些

目录 数值函数 字符串函数 日期函数 转换函数 聚合函数 分析函数 Oracle数据库提供了大量的内置函数,这些函数可以分为多个类别,每个类别都有特定的用途。以下是一些常见的Oracle函数及其简要描述。 数值函数 ABS(n):返回数字的绝对值。 CEIL(n)或CEILING(n):返回大…

Python异步爬虫批量下载图片-协程

import aiofiles import aiohttp import asyncio import requests from lxml import etree from aiohttp import TCPConnectorclass Spider:def __init__(self, value):# 起始urlself.start_url value# 下载单个图片staticmethodasync def download_one(url):name url[0].spl…

Redis 5种常用数据类型

目录 Redis简介 1.字符串 string 2.哈希 hash 3.列表 list 4.集合 set 5.有序集合 sorted set / zset Redis简介 Redis&#xff0c;全称Remote Dictionary Server&#xff0c;是一个开源的、内存中的数据结构存储系统。它可以用作数据库、缓存和消息中间件&#xff0c;支…

Hash String 学习笔记

目录 咕咕咕 Trie 树/字典树 P8306 【模板】字典树 咕咕咕&#xff08;感觉比较简单&#xff08;吗&#xff09;&#xff09;&#xff08;我才不会说是我懒呢&#xff09; KMP 一个求最长公共前后缀的东西 P3375 【模板】KMP 写法一 #include<bits/stdc.h> using name…

【JavaScript脚本宇宙】表格大变身:探秘JavaScript库的数据表格魔法

优化数据展示&#xff1a;精选JavaScript表格增强库对比 前言 在现代Web开发中&#xff0c;利用各种库和框架来增强数据表格的功能已经成为常态。通过使用特定的JavaScript库和插件&#xff0c;开发人员可以轻松地实现交互性强、美观且高性能的数据表格&#xff0c;从而提升用…

JavaScript前端技术入门教程

引言 在前端开发的广阔天地中&#xff0c;JavaScript无疑是最耀眼的一颗明星。它赋予了网页动态交互的能力&#xff0c;让网页从静态的文本和图片展示&#xff0c;进化为可以与用户进行实时交互的丰富应用。本文将带您走进JavaScript的世界&#xff0c;为您提供一个入门级的教…

Nginx访问日志

Nginx日志是Nginx Web服务器产生的记录文件&#xff0c;主要用于跟踪和分析服务器的访问情况以及错误信息。Nginx日志主要分为两大类&#xff1a;访问日志 (access_log): 访问日志记录了每一次客户端对Nginx服务器的HTTP请求的详细信息&#xff0c;这对于统计分析、流量监控、用…