【Spring源码分析】扫描并注册BeanDefinition逻辑

扫描并注册BeanDefinition逻辑

  • 一、ClassPathBeanDefinitionScanner 扫描源码分析
    • doScan 扫描的具体逻辑
      • findCandidateComponents 方法解析
      • generateBeanName 方法解析
      • checkCandidate 方法解析
  • 二、总结

阅读此需阅读下面这些博客先
【Spring源码分析】Bean的元数据和一些Spring的工具
【Spring源码分析】BeanFactory系列接口解读

扫描源码一般是接触Spring源码时大家首先去接触的,因为它是去扫描且过滤出符合的条件的Bean的元数据的(注意这里说的是Bean的元数据,并非全是类的元数据,在此过程中用了ASM技术去提取类的元数据进行操作),比如满足includefilter,不是接口,抽象类等等才会被加入候选…

这当然也是我看源码时首先看的,但是我自己分析做的笔记呢,只是在本地,其实很多都是在本地写 typora 的,比较快,写 CSDN 也是有点耗时滴~大家想一起学习的话可以私聊我加我,若觉得自己学啥好累,我本地其实有相关笔记的,就是可能潦草了点。

本来这没打算写博客的,但是源码这东西都是一环扣一环的,这又是比较重要的一个阶段,所以还是阐述一下吧,可能有些潦草,大伙需要自己边看,然后看这种博客会印象更深。

一、ClassPathBeanDefinitionScanner 扫描源码分析

doScan 扫描的具体逻辑

下面是其方法的代码解释,随后再具体分析:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();for (String basePackage : basePackages) {// 过滤掉一些资源,得到预选的BeanDefinitionSet<BeanDefinition> candidates = findCandidateComponents(basePackage);for (BeanDefinition candidate : candidates) {// 设置bean是多例还是单例ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);candidate.setScope(scopeMetadata.getScopeName());// 获取到 BeanName,若存在@Component 注解则获取其value属性值,若没有则// Introspector.decapitalize(shortClassName);// 若0、1为大写则整个类名作为beanName,若不是则首字母小写String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);// 设置一堆默认值if (candidate instanceof AbstractBeanDefinition) {postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {// 解析@Lazy、@Primary、@DependsOn、@Role、@DescriptionAnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 检查Spring容器中是否已经存在该beanNameif (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);// 注册registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;}

findCandidateComponents 方法解析

核心还是在 scanCandidateComponents 方法中:

在这里插入图片描述

源码解析:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {// 获取basePackage下所有的文件资源String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}if (resource.isReadable()) {try {MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);// excludeFilters、includeFilters判断// 一些过滤操作if (isCandidateComponent(metadataReader)) { // @Component-->includeFilters判断ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);sbd.setSource(resource);// 这里进行再次过滤,允许超类或者静态类if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}candidates.add(sbd);}}}}}return candidates;}

这边扫描定义了ExcludedeFilters:

@ComponentScan(basePackages = "com.powernode",excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = UserService.class)})

则对应的 excludeFilters 为,若被它匹配到返回false,则不会添加到入选候选集candidates里。

在这里插入图片描述

includeFilter 在初始化就会注册默认的过滤器,添加@Component至内

在这里插入图片描述

则存在@Component注解且有Condition判断且判断为true的话就会放行通过:

在这里插入图片描述

  • 先通过给的路径扫描所有资源;
  • 遍历资源通过 ASM 技术获取到对应的元数据,然后封装成 ScannedGenericBeanDefinition;
  • 1、随后判断是否可以通过 includefilters 且若有 Conditional 注解的话判断是否匹配成功,且不被 excludefilters 过滤,则放行;
  • 2、再次过滤,允许超类或者静态类
  • 放入候选集 candidates 中,最后返回

generateBeanName 方法解析

具体源码如下:

	public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {if (definition instanceof AnnotatedBeanDefinition) {// 获取注解所指定的beanNameString beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);if (StringUtils.hasText(beanName)) {// Explicit bean name found.return beanName;}}// Fallback: generate a unique default bean name.// 生成一个默认的beanNamereturn buildDefaultBeanName(definition, registry);}

获取@Component中的beanName实现也很简单,就是遍历类上的所有注解,看有没有,有就取,没有就返回null回去:

在这里插入图片描述

要为空就会生成一个默认的beanName,默认生成则通过内省API Introspector.decapitalize(shortClassName); 直接生成,里面的逻辑就是若首字母和次字母是大写就直接返回类名,若不是就首字母小写

在这里插入图片描述

checkCandidate 方法解析

	protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) throws IllegalStateException {if (!this.registry.containsBeanDefinition(beanName)) {return true;}BeanDefinition existingDef = this.registry.getBeanDefinition(beanName);BeanDefinition originatingDef = existingDef.getOriginatingBeanDefinition();if (originatingDef != null) {existingDef = originatingDef;}// 是否兼容,如果兼容返回false表示不会重新注册到Spring容器中,如果不冲突则会抛异常。// 就是看看是不是同一个类的 beanDefinition// 就是同一类是不是被多次加载了,出现多次scan会有这种现象if (isCompatible(beanDefinition, existingDef)) {return false;}throw new ConflictingBeanDefinitionException("Annotation-specified bean name '" + beanName +"' for bean class [" + beanDefinition.getBeanClassName() + "] conflicts with existing, " +"non-compatible bean definition of same name and class [" + existingDef.getBeanClassName() + "]");}
  • 就判断容器内是否有相同的 beanDefinition,以beanName进行判断,如果没有就直接返回true,同意注册;
  • 否则看是否是同一个类多次加载,是的话就返回false不注册;
  • 再则就直接抛 ConflictBeanDefinitionException 异常。

检查如果通过的话就把 beanName 放入到 beanDefinitionNames 集合中,把 BeanDefinition 放入到 beanDefinitionMap 中。

二、总结

  • 首先是经过扫描获取到候选的 BeanDefinition 集(在选举候选集的时候,会经过 includefilter 判断,它内部初始化的时候会放入 @Component 注解进去进行检举,且过滤掉 excludefilter);
  • 填充 scope;
  • 获取 beanName;
  • 初始化 BeanDefinition 中的一些属性;
  • 解析@Lazy、@Primary、@DependsOn、@Role、@Description 到 BeanDefinition 中。
  • 注册。

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

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

相关文章

SpringBoot项目中简单使用虚拟机Redis

目录 步骤大致如下&#xff1a; 一.在pom文件中加入redis依赖 二.在虚拟机上打开我们下载好的Redis。开启服务器端并获取虚拟机ip地址 三.在项目配置。 四&#xff1a;使用redis 测试 redis是一个以键值对存储的NoSQL。被数百万开发人员用作缓存、矢量数据库、文档数据库、…

AcWing 68:0到n-1中缺失的数字 ← 二分

【题目来源】https://www.acwing.com/problem/content/64/【题目描述】 一个长度为 n−1 的递增排序数组中的所有数字都是唯一的&#xff0c;并且每个数字都在范围 0 到 n−1 之内。 在范围 0 到 n−1 的 n 个数字中有且只有一个数字不在该数组中&#xff0c;请找出这个数字。【…

vivado 定义板文件板

定义板文件板 &#xff1c;board&#xff1e;标记是板文件的根。它包括识别基本信息的属性关于董事会。 <board schema_version"2.1" vendor"xilinx.com" name"kc705" display_name"Kintex-7 KC705 Evaluation Platform" url&qu…

用C语言实现简单的三子棋游戏

目录 1 -> 模块简介 2 -> test.c 3 -> game.c 4 -> game.h 1 -> 模块简介 test.c:测试游戏逻辑 game.c: 函数的实现 game.h:函数的声明 2 -> test.c #define _CRT_SECURE_NO_WARNINGS 1#include "game.h";void menu() {printf("****…

Harmony Ble蓝牙App(四)描述符

Harmony Ble蓝牙App&#xff08;四&#xff09;描述符 前言正文一、优化二、描述① 概念② 描述提供者③ 显示描述符 三、源码 前言 上一篇中了解了特性和属性&#xff0c;同时显示设备蓝牙服务下的特性和属性&#xff0c;本文中就需要来使用这些特性和属性来完成一些功能。 正…

FaFu--练习复盘--1

1、输出图形及二维数组应用 1.1.输出图形 描述 编写程序打印n行如下图形&#xff0c;其中1≤n≤500。 输入用例 7 输出用例 具体实现 #include"stdio.h" int main(){int n,i,j;scanf("%d",&n);for(i 1; i< n;…

软考系分之计算机网络通信方向、同步和交换

文章目录 1、概述2、通信方向和同步3、交换方式4、总结 1、概述 本篇依旧是一图概括主要考察的知识点&#xff0c;包括通信方向&#xff08;单工、半双工、全双工&#xff09;&#xff0c;同步方式和数据交换等。 2、通信方向和同步 通信方向&#xff08;单工、半双工、全双工…

【react】创建react项目+项目结构

使用create-react-app快速搭建开发环境 create-react-app是一个快速创建React开发环境的工具&#xff0c;底层由Webpack构建&#xff0c;封装了配置细节 npx create-react-app react_hm执行命令后开始创建 创建好执行cd react_hm npm start 当看到webpack compiled successfu…

spring boot shardingsphere mybatis-plus druid mysql 搭建mysql数据库读写分离架构

spring boot shardingsphere mybatis-plus druid mysql 搭建mysql数据库读写分离架构 ##关于window mysql主从搭建简单教程 传送门 window mysql5.7 搭建主从同步环境-CSDN博客 ##父pom.xml <?xml version"1.0" encoding"UTF-8"?> <project…

傲空间私有部署Windows指南

推荐阅读 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;一&#xff09; 智能化校园&#xff1a;深入探讨云端管理系统设计与实现&#xff08;二&#xff09; 安装 docker 请下载对应的 Docker&#xff0c;安装完成后启动。 Docker Desktop for Windows…

领略指针之妙

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

MySQL主从集群

MySQL主从集群 主从模式、集群模式&#xff0c;都是在一个项目中使用多个mysql节点进行存储和读取数据。 当单机模式部署&#xff0c;不满足安全性、高可用、高并发等需求的时候&#xff0c;就需要考虑主从模式或者集群模式部署。 什么是主从模式&#xff1f; 主从模式&…

[3D]菜板上的鱼

本来想画条鲨鱼&#xff0c;结果成了条菜板上的鱼。 VS&#xff01; ** VS&#xff01; ** 【扭曲】更像菜板上的鱼了。

[C#]winform部署openvino官方提供的人脸检测模型

【官方框架地址】 https://github.com/sdcb/OpenVINO.NET 【框架介绍】 OpenVINO&#xff08;Open Visual Inference & Neural Network Optimization&#xff09;是一个由Intel推出的&#xff0c;针对计算机视觉和机器学习任务的开源工具套件。通过优化神经网络&#xff…

C++设计模式之 模板方法模式

【声明】本题目来源于卡码网&#xff08;题目页面 (kamacoder.com)&#xff09; 【提示&#xff1a;如果不想看文字介绍&#xff0c;可以直接跳转到C编码部分】 【设计模式大纲】 【简介】 --什么是模板方法模式&#xff08;第18种设计模式&#xff09; 模板方法模式&#xff0…

【MySQL】——关系数据库标准语言SQL(大纲)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

51单片机8*8点阵屏

8*8点阵屏 8*8点阵屏是一种LED显示屏&#xff0c;它由8行和8列的LED灯组成。每个LED灯的开闭状态都可以独立控制&#xff0c;从而可以显示出数字、字母、符号、图形等信息。 8*8点阵屏的原理是通过行列扫描的方式&#xff0c;控制LED灯的亮灭&#xff0c;从而显示出所需的图案或…

多线程编程常见面试题讲解(锁策略,CAS策略,synchronized原理,JUC组件,集合类)

&#x1f495;"跑起来就有意义"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;多线程编程常见面试题讲解 hello各位朋友们,最近笔者刚刚结束了学校的期末考试,现在回来继续更新啦!!! 今天要学习的是多线程常见面试题讲解,这些内容都是面试中常考的…

在k8s上部署ClickHouse

概述 clickhouse的容器化部署&#xff0c;已经有非常成熟的生态了。在一些互联网大厂也已经得到了大规模的应用。 clickhouse作为一款数据库&#xff0c;其容器化的主要难点在于它是有状态的服务&#xff0c;因此&#xff0c;我们需要配置PVC。 目前业界比较流行的部署方式有…

SaaS多租户篇

文章目录 1. 多租户是什么2. 技术组件2.1 如何实现多租户的DB封装2.2 如何实现多租户的redis封装2.3 如何实现多租户的Web和Security封装 1. 多租户是什么 2. 技术组件 2.1 如何实现多租户的DB封装 2.2 如何实现多租户的redis封装 2.3 如何实现多租户的Web和Security封装