如何看穿 SpringBoot 内部 SpringFactoriesLoader 破坏双亲委派的骚操作

冷漠man

冷漠man

2026-05-29

928人浏览

原创

springboot并未破坏双亲委派,而是通过springfactoriesloader结合线程上下文类加载器(tccl)实现受控的逆向委派:框架类由高层加载器加载,却主动委托appclassloader加载用户扩展类,本质是遵循jvm spi契约的标准实践。

如何看穿 springboot 内部 springfactoriesloader 破坏双亲委派的骚操作

SpringBoot 并没有直接“破坏”双亲委派,而是通过 SpringFactoriesLoader 配合线程上下文类加载器(TCCL)实现了一种受控的逆向委派——它不改写 loadClass(),也不绕过父加载器,而是让高层加载器(如 Bootstrap 或 Extension)在需要时,主动委托当前应用的类加载器去加载用户定义的扩展类。这才是真正看穿它的关键。

SpringFactoriesLoader 本身不破坏委派,它只是个读取工具

SpringFactoriesLoader 的核心逻辑非常简单:调用 ClassLoader.getResources("META-INF/spring.factories"),遍历所有匹配的资源,解析键值对,再用同一个 ClassLoader 去加载 value 中的类名。它不做任何类加载决策,也不切换加载器——它用谁的 ClassLoader,取决于你传入的 ClassLoader 参数,或默认使用当前线程的 TCCL。

常见调用方式:

  • SpringFactoriesLoader.loadFactoryNames(..., classLoader) —— 显式传入加载器
  • SpringFactoriesLoader.loadSpringFactories(ClassLoader) —— 默认用 Thread.currentThread().getContextClassLoader()

也就是说,破坏与否,不在 SpringFactoriesLoader 代码里,而在谁调用它、用哪个 ClassLoader 调用它

真正的“骚操作”藏在自动配置启动阶段

SpringBoot 启动时,SpringApplication.run() 会触发 getSpringFactoriesInstances(ApplicationContextInitializer.class) 等一系列加载。此时线程上下文类加载器(TCCL)已被设为 AppClassLoader(即加载你 application.jar 的那个加载器)。于是:

  • 哪怕 SpringFactoriesLoader 类本身由 Bootstrap 或 Ext 加载器加载(比如来自 spring-boot 的 jar 在 classpath),它读取到 META-INF/spring.factories 后,仍会用 TCCL(也就是你的 AppClassLoader)去加载其中声明的 org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration 这类用户级自动配置类
  • 这就实现了“高层代码(框架)调用底层加载器(应用)来加载扩展类”,是典型的 逆向委派,也是 JVM 官方认可的 SPI 模式

对比 JDBC 的 DriverManager,逻辑完全一致

java.sql.DriverManager 一样:

  • DriverManager 类由 Bootstrap 加载(在 rt.jar 中)
  • 但它内部调用 ServiceLoader.load(Driver.class) 时,默认使用 TCCL
  • 因此能加载 mysql-connector-java.jar 中的 com.mysql.cj.jdbc.Driver(由 AppClassLoader 加载)
  • SpringFactoriesLoader 就是 Spring 版的 ServiceLoader,只是配置文件从 META-INF/services/xxx 换成了 META-INF/spring.factories,机制一模一样

为什么这不算“暴力破坏”,而是一种设计契约

它没覆盖 loadClass(),没屏蔽父委派,也没让 Bootstrap 去直接读你项目里的 class 文件。它依赖的是:

  • JVM 允许线程持有自己的 ClassLoader(TCCL 是公开 API)
  • 框架层约定:SPI 扩展必须由当前应用环境的类加载器负责加载
  • 所有主流容器(Tomcat、Spring Boot、Dubbo)都遵守这个契约,而非各自 hack

所以这不是漏洞利用,而是标准玩法——你看穿的不是“破坏”,而是框架如何借力 TCCL 实现可插拔

相关专题

更多
C语言变量命名
C语言变量命名

c语言变量名规则是:1、变量名以英文字母开头;2、变量名中的字母是区分大小写的;3、变量名不能是关键字;4、变量名中不能包含空格、标点符号和类型说明符。php中文网还提供c语言变量的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

1349

3

c语言入门自学零基础
c语言入门自学零基础

C语言是当代人学习及生活中的必备基础知识,应用十分广泛,本专题为大家c语言入门自学零基础的相关文章,以及相关课程,感兴趣的朋友千万不要错过了。

2023.07.25

1536

9

c语言运算符的优先级顺序
c语言运算符的优先级顺序

c语言运算符的优先级顺序是括号运算符 > 一元运算符 > 算术运算符 > 移位运算符 > 关系运算符 > 位运算符 > 逻辑运算符 > 赋值运算符 > 逗号运算符。本专题为大家提供c语言运算符相关的各种文章、以及下载和课程。

2023.08.02

691

5

c语言数据结构
c语言数据结构

数据结构是指将数据按照一定的方式组织和存储的方法。它是计算机科学中的重要概念,用来描述和解决实际问题中的数据组织和处理问题。数据结构可以分为线性结构和非线性结构。线性结构包括数组、链表、堆栈和队列等,而非线性结构包括树和图等。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.09

551

4

c语言random函数用法
c语言random函数用法

c语言random函数用法:1、random.random,随机生成(0,1)之间的浮点数;2、random.randint,随机生成在范围之内的整数,两个参数分别表示上限和下限;3、random.randrange,在指定范围内,按指定基数递增的集合中获得一个随机数;4、random.choice,从序列中随机抽选一个数;5、random.shuffle,随机排序。

2023.09.05

909

5

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

2023.09.20

1128

7

c语言get函数的用法
c语言get函数的用法

get函数是一个用于从输入流中获取字符的函数。可以从键盘、文件或其他输入设备中读取字符,并将其存储在指定的变量中。本文介绍了get函数的用法以及一些相关的注意事项。希望这篇文章能够帮助你更好地理解和使用get函数 。

2023.09.20

1662

8

c数组初始化的方法
c数组初始化的方法

c语言数组初始化的方法有直接赋值法、不完全初始化法、省略数组长度法和二维数组初始化法。详细介绍:1、直接赋值法,这种方法可以直接将数组的值进行初始化;2、不完全初始化法,。这种方法可以在一定程度上节省内存空间;3、省略数组长度法,这种方法可以让编译器自动计算数组的长度;4、二维数组初始化法等等。

2023.09.22

5513

6

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

2023.09.22

419

3

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Redis6入门到精通超详细教程
Redis6入门到精通超详细教程

共47课时 | 6.7万人学习