Spring源码篇(九)自动配置扫描class的原理

文章目录

  • 前言
  • ClassLoader
  • 如何加载jar包里的class
  • 自动配置扫描class的原理
    • spring中的加载方式
    • 源码
    • 总结

前言

spring是怎样通过@ComponentScan,或者自动配置扫描到了依赖包里class的?

ClassLoader

这里涉及到了class Loader的机制,有些复杂,jdk中提供默认3个class Loader:

  • Bootstrap ClassLoader:加载jdk核心类库;加载%JAVA_HOME\lib%下的jar;
  • ExtClassLoader:加载jdk扩展类库;加载%JAVA_HOME\lib\ext%下的jar;
  • AppClassLoader:加载classpath下的class,以及关联到maven仓库里的jar;

AppClassLoaderExtClassLoader父类都是URLClassLoader,我们自定义也是继承URLClassLoader进行扩展的;

所以,当我们使用类加载器加载资源时,它会找上面这些路径,而AppClassLoader是找当前执行程序的classpath,也就是我们target/classes目录,如果有是maven引用了其他依赖包,那么也会将maven地址下的依赖包的路径加到AppClassLoaderURL里,如果是多模块的项目,还会把引用的其他模块下target/classes的目录也加进来。

image-20230804144522488

image-20230804144419885

如何加载jar包里的class

假设需要获取一个jar包里的class该如何?

如下4个步骤即可:

    public static void main(String[] args) throws Exception {String packageName = "com.liry.springplugin";// 1. 转换为 com/liry/springpluginString packagePath = ClassUtils.convertClassNameToResourcePath(packageName);// 2. 通过类加载器加载jar包URL
//        ClassLoader classLoader = Test.class.getClassLoader();ClassLoader classLoader = new URLClassLoader(new URL[]{new URL("file:E:\\workspace\\git\\test-plugin\\spring-plugin\\target\\spring-plugin-1.0-SNAPSHOT.jar")});URL resources = classLoader.getResource(packagePath);// 3. 打开资源通道JarFile jarFile = null;URLConnection urlConnection = resources.openConnection();if (urlConnection instanceof java.net.JarURLConnection) {java.net.JarURLConnection jarURLConnection = (java.net.JarURLConnection) urlConnection;jarFile = jarURLConnection.getJarFile();}// 定义一个结果集List<String> resultClasses = new ArrayList<>();// 4. 遍历资源文件Enumeration<JarEntry> entries = jarFile.entries();while (entries.hasMoreElements()) {JarEntry entry = entries.nextElement();// 文件全路径String path = entry.getName();// 判断是否在指定包路径下,jar包里有多层目录、MF文件、class文件等多种文件信息if (path.startsWith(packagePath)) {// 使用spring的路径匹配器匹配class文件if (path.endsWith(".class")) {resultClasses.add(path);}}}resultClasses.forEach(System.out::println);}

image-20230803174544910

说明一下,加载jar包的问题;

上面给出了两种方式

第一种:使用类加载加载

ClassLoader classLoader = Test.class.getClassLoader();

第二种:使用URLClassLoader加载

ClassLoader classLoader = new URLClassLoader(new URL[]{new URL("file:E:\\workspace\\git\\test-plugin\\spring-plugin\\target\\spring-plugin-1.0-SNAPSHOT.jar")});

这两种方式不同之处在于,查找jar的路径,第一种方式因为我测试项目使用的maven,在pom.xml里引入了spring-plugin-1.0-SNAPSHOT的包,所以才能通过类加载器直接进行加载,这是因为使用maven,maven引用的依赖路径会被加入到AppClassLoader种,然后使用Test.class.getClassLoader()去加载class时,会委派给AppClassLoader进行加载,才会加载到。

所以,如果不是在maven种引入的包,使用第二种方式。

自动配置扫描class的原理

那么这里简单的走一下自动配置流程:

  1. 启动类上有@EanbleConfiguration,会读取META-INF/spring.factories里配置的配置类
  2. 读取后解析成beanDefinition,然后判断是否配置类,如果是就找配置类注解(@ComponentScan @Import @Component @Service等这样的注解),如果配置类有扫描class的注解,就去扫描
  3. 最后得到所有的bean的beanDefinition

spring中的加载方式

在spring中加载class的方式就是上面的方式,我这里就在上面示例的基础上增加一些细节,如下:

 static PathMatcher pathMatcher = new AntPathMatcher();public static void getClassResource() throws Exception {String packageName = "com.liry.springplugin";// 1. 转换为 com/liry/springpluginString packagePath = ClassUtils.convertClassNameToResourcePath(packageName);// 2. 通过类加载器加载jar包URLClassLoader classLoader = Test.class.getClassLoader();URL resources = classLoader.getResource(packagePath);// spring的资源文件对象UrlResource rootResource = new UrlResource(resources);// 3. 打开资源通道JarFile jarFile = null;URLConnection urlConnection = resources.openConnection();if (urlConnection instanceof java.net.JarURLConnection) {java.net.JarURLConnection jarURLConnection = (java.net.JarURLConnection) urlConnection;jarFile = jarURLConnection.getJarFile();}// 定义一个结果集List<Resource> resultClasses = new ArrayList<>();// 4. 遍历资源文件Enumeration<JarEntry> entries = jarFile.entries();// 包路径以 / 结尾拥于后面进行替换if (packagePath.endsWith("/")) {packagePath += "/";}while (entries.hasMoreElements()) {JarEntry entry = entries.nextElement();// 文件全路径String path = entry.getName();// 判断是否在指定包路径下,jar包里有多层目录、MF文件、class文件等多种文件信息if (path.startsWith(packagePath)) {// 这里去掉指定的包名,比如com/liry/springplugin/AutoConfig.class,结果就是AutoConfig.classString relativePath = path.substring(packagePath.length() + 1);// 使用spring的路径匹配器匹配class文件if (pathMatcher.match("**/*.class", relativePath)) {Resource relative = rootResource.createRelative(relativePath);resultClasses.add(relative);}}}resultClasses.forEach(d -> System.out.println(d.getFilename()));}

image-20230804132300035

上面这段已经和spring中加载class的方式是一样的了,对应源码位置:

org.springframework.core.io.support.PathMatchingResourcePatternResolver#doFindPathMatchingJarResources

源码

如果配置类存在@ComponentScan,会拿到注解里的值,也就是basePackages,然后走到:

org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan

这里就是扫描所有的class,然后再构建出beanDefinition对象

image-20230804135544195

org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents

image-20230804141622499

image-20230804141650001

org.springframework.core.io.support.PathMatchingResourcePatternResolver#findPathMatchingResources

image-20230804141739226

最后走到这里,这里就是读取class的地方,这里的逻辑就和上面的例子是一样的

org.springframework.core.io.support.PathMatchingResourcePatternResolver#doFindPathMatchingJarResources

image-20230804142148041

这一步就是在匹配@ComponentScan的basePackages下的class。

总结

  1. 当前项目路径会被加载到AppClassLoader,而且使用maven,对应的maven里的jar路径也会被加载到AppClassLoader中;
  2. 注册配置类,启动类为入口;
  3. 通过配置类找到找到@ComponentScan扫描指定路径class,如果找到配置类,还有@Import开头的注解,以及@EnableConfiguration这些注解,也都是把class找到,然后判断是否配置类,如果是就再去找这些注解,以此循环;
  4. 如果是@EnableConfiguration注解,读取META-INF/spring.factories文件里的value,并解析成配置类,再循环;通样的如果是@Import注解,引入的是一个非DeferredImportSelector的配置类也是如此,
  5. 最后项目中就存在所有的bean的beanDefinition

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

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

相关文章

第125天:内网安全-隧道技术SMBICMP正反向连接防火墙出入规则上线

知识点 #知识点&#xff1a; 1、入站规则不出网上线方案 2、出站规则不出网上线方案 3、规则-隧道技术-SMB&ICMP-隧道技术&#xff1a;解决不出网协议上线的问题&#xff08;利用出网协议进行封装出网&#xff09; -代理技术&#xff1a;解决网络通讯不通的问题&#xff0…

Kali部署dvwa和pikachu靶场

kali换源 进入 vim /etc/apt/sources.list deb https://mirrors.aliyun.com/kali kali-rolling main non-free contrib deb-src https://mirrors.aliyun.com/kali kali-rolling main non-free contrib替换完后更新源 apt-get upadteDVWA靶场环境搭建 使用git从github上把DV…

模板方法设计模式(C++)

定义 定义一个操作中的算法的骨架(稳定&#xff09;,而将一些步骤延迟(变化&#xff09;到子类中。Template Method使得子类可以不改变(复用&#xff09;一个算法的结构即可重定义(override重写)该算法的某些特定步骤。 ——《设计模式》GoF Template Method模式是一种非常基…

Windows Server 2012 R2 安装 Oracle RAC 11g R2

Windows Server 2012 R2 安装 Oracle RAC 11g R2 环境准备安装系统设置虚拟网络配置虚拟机网卡开机进行系统配置关闭防火墙设置网络系统高级设置修改注册表修改计算机名称设置账户控制RAC1 和 RAC2 的磁盘共享修改 hosts同步时间在 RAC1 RAC2 DATA 中安装 .net3.5在 DATA 中搭建…

设计模式行为型——中介者模式

目录 什么是中介者模式 中介者模式的实现 中介者模式角色 中介者模式类图 中介者模式代码实现 中介者模式的特点 优点 缺点 使用场景 注意事项 实际应用 什么是中介者模式 中介者模式&#xff08;Mediator Pattern&#xff09;属于行为型模式&#xff0c;是用来降低…

浪潮服务器硬盘指示灯显示黄色的服务器数据恢复案例

服务器数据恢复环境&#xff1a; 宁夏某市某单位的一台浪潮服务器&#xff0c;该服务器中有一组由6块SAS硬盘组建的RAID5阵列。 服务器上存放的是Oracle数据库文件&#xff0c;操作系统层面划分了1个卷。 服务器故障&初检&#xff1a; 服务器在运行过程中有两块磁盘的指示灯…

精选5个AI绘画工具,灵感与创意迸发!

伴随着AI技术的发展&#xff0c;AI绘画工具也走进了人们的视线&#xff0c;今天本文将为大家推荐5个最近火爆的AI绘画神器&#xff0c;一起来看看吧&#xff01; 1、即时灵感 即时灵感是一个国产的AI绘画神器&#xff0c;非常受国内设计师的欢迎。它支持设计师使用中文描述词…

C 语言高级2-多维数组,结构体,递归操作

1. 多维数组 1.1 一维数组 元素类型角度&#xff1a;数组是相同类型的变量的有序集合内存角度&#xff1a;连续的一大片内存空间 在讨论多维数组之前&#xff0c;我们还需要学习很多关于一维数组的知识。首先让我们学习一个概念。 1.1.1 数组名 考虑下面这些声明&#xff1…

【图论】强连通分量进阶

一.作用 强连通分量可以判断环和进行缩点。还有一系列作用.... 这篇文章介绍缩点 二.题目 https://www.luogu.com.cn/problem/P2341 三.思路 我们分析可以知道当一个点没有出度时&#xff0c;则为最受欢迎的牛。但如果有多个出度&#xff0c;则没有最受欢迎的牛。 这是只有…

Golang之路---02 基础语法——函数

函数 由于Golang语言是编译型语言&#xff0c;所以函数编写的顺序是无关紧要的&#xff0c;它不像 Python 那样&#xff0c;函数在位置上需要定义在调用之前。 函数定义 func function_name( [parameter list] ) [return_types] {函数体 }参数解释&#xff1a; func&#x…

二、搜索与图论6:Dijkstra 模板题+算法模板(Dijkstra求最短路 I, Dijkstra求最短路 II,1003 Emergency)

文章目录 算法模板Dijkstra题目代码模板朴素dijkstra算法堆优化版dijkstra 树与图的存储(1) 邻接矩阵&#xff1a;(2) 邻接表&#xff1a;关于e[],ne[],h[]的理解 关于堆的原理与操作 模板题Dijkstra求最短路 I原题链接题目思路题解 Dijkstra求最短路 II原题链接题目思路题解 1…

RocksDB-db_bench源码(二):mixgraph负载的源码解读

文章目录 key 的分布key_range 的分布QPS 的分布 最近在用 mixgraph 负载对 RocksDB 进行测试&#xff0c;其是 facebook 提供的一种基于 db_bench 输入的负载&#xff0c;整合在了 RocksDB 的源码中&#xff0c;同 fillrandom、fillseq 等等一样的调用流程。 苦于 mixgraph 的…