Spring Boot 类加载机制深度解析
Spring Boot 类加载机制深度解析
前言
在 Java 应用开发中,类加载机制是一个重要且复杂的话题。Spring Boot 作为现代 Java 开发的主流框架,其类加载机制更是值得深入了解。本文将从基础概念到实际应用,全面解析 Spring Boot 的类加载机制。
1. Java 类加载基础
1.1 什么是类加载器
类加载器(ClassLoader)是 Java 虚拟机用来加载 Java 类的组件。它负责读取 Java 字节码并转换为 java.lang.Class
类的实例。
1.2 类加载器的层次结构
Java 采用双亲委派模型,类加载器形成树状层次结构:
Bootstrap ClassLoader (启动类加载器)↓
Extension ClassLoader (扩展类加载器)↓
Application ClassLoader (应用程序类加载器)↓
Custom ClassLoader (自定义类加载器)
1.3 双亲委派模型
双亲委派模型的工作流程:
- 当一个类加载器收到类加载请求时,首先将请求委派给父类加载器
- 只有当父类加载器无法完成加载时,子类加载器才会尝试自己加载
- 这种机制保证了 Java 核心类库的安全性和唯一性
2. Spring Boot 类加载特点
2.1 Fat JAR 结构
Spring Boot 应用通常打包为 Fat JAR(胖 JAR),包含:
- 应用代码
- 所有依赖的 JAR 包
- Spring Boot 加载器代码
my-application.jar
├── BOOT-INF/
│ ├── classes/ # 应用类文件
│ ├── lib/ # 依赖 JAR 包
│ └── classpath.idx # 类路径索引
├── META-INF/
│ └── MANIFEST.MF # 清单文件
└── org/springframework/boot/loader/ # Spring Boot 加载器
2.2 Spring Boot 类加载器
Spring Boot 提供了专门的类加载器来处理 Fat JAR:
LaunchedURLClassLoader
- 继承自
URLClassLoader
- 专门用于加载 Fat JAR 中的类和资源
- 支持嵌套 JAR 的加载
JarFileArchive
- 用于处理 JAR 文件的抽象
- 支持嵌套 JAR 文件的访问
3. Spring Boot 启动过程中的类加载
3.1 启动流程
3.2 关键组件
JarLauncher
public class JarLauncher extends ExecutableArchiveLauncher {public static void main(String[] args) throws Exception {new JarLauncher().launch(args);}@Overrideprotected boolean isNestedArchive(Archive.Entry entry) {return entry.isDirectory() ? entry.getName().equals("BOOT-INF/classes/"): entry.getName().startsWith("BOOT-INF/lib/");}
}
LaunchedURLClassLoader
public class LaunchedURLClassLoader extends URLClassLoader {public LaunchedURLClassLoader(URL[] urls, ClassLoader parent) {super(urls, parent);}@Overrideprotected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {// 实现特定的类加载逻辑return super.loadClass(name, resolve);}
}
4. 类加载顺序和优先级
4.1 加载顺序
- Bootstrap ClassLoader: 加载 JVM 核心类
- Extension ClassLoader: 加载扩展类
- LaunchedURLClassLoader: 加载应用类和依赖
- 首先加载
BOOT-INF/classes/
中的应用类 - 然后加载
BOOT-INF/lib/
中的依赖 JAR
- 首先加载
4.2 类路径优先级
1. BOOT-INF/classes/ # 应用类 (最高优先级)
2. BOOT-INF/lib/ # 依赖 JAR 包
3. System ClassPath # 系统类路径
5. 常见问题和解决方案
5.1 类冲突问题
问题描述: 不同 JAR 包中存在相同的类,导致类加载冲突。
解决方案:
<!-- 在 pom.xml 中排除冲突的依赖 -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><exclusions><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions>
</dependency>
5.2 ClassNotFoundException
常见原因:
- 缺少必要的依赖
- 类路径配置错误
- Maven/Gradle 依赖版本冲突
解决方法:
# 查看 JAR 包内容
jar -tf myapp.jar | grep ClassName# 检查类路径
java -cp myapp.jar -verbose:class MainClass
5.3 内存溢出问题
原因: 大量类加载导致 Metaspace 溢出
解决方案:
# 调整 JVM 参数
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -jar myapp.jar
6. 最佳实践
6.1 依赖管理
<!-- 使用 Spring Boot BOM 管理版本 -->
<dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.7.0</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>
6.2 自定义类加载器
@Component
public class CustomClassLoader extends URLClassLoader {public CustomClassLoader(URL[] urls) {super(urls, CustomClassLoader.class.getClassLoader());}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 自定义类加载逻辑return super.findClass(name);}
}
6.3 监控和调试
// 获取类加载信息
ClassLoader classLoader = this.getClass().getClassLoader();
System.out.println("Class loader: " + classLoader.getClass().getName());// 查看类加载路径
if (classLoader instanceof URLClassLoader) {URLClassLoader urlClassLoader = (URLClassLoader) classLoader;URL[] urls = urlClassLoader.getURLs();for (URL url : urls) {System.out.println("Classpath: " + url.toString());}
}
7. 性能优化
7.1 类加载优化
-
减少不必要的依赖: 移除未使用的 JAR 包
-
使用 Maven/Gradle 的依赖分析工具:
mvn dependency:analyze gradle dependencies
-
启用类数据共享 (CDS):
java -Xshare:on -jar myapp.jar
7.2 启动时间优化
# application.properties
spring.jmx.enabled=false
spring.main.lazy-initialization=true
8. 调试工具和技巧
8.1 JVM 参数
# 查看类加载详情
-verbose:class# 查看类加载时间
-XX:+TraceClassLoading
-XX:+TraceClassUnloading# 分析类加载性能
-XX:+LogVMOutput
-XX:+UseCompressedOops
8.2 Spring Boot Actuator
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
访问 /actuator/beans
查看已加载的 Bean 信息。
9. 总结
Spring Boot 的类加载机制是一个复杂但精心设计的系统,它:
- 简化了部署: 通过 Fat JAR 实现一键运行
- 保证了隔离: 通过自定义类加载器避免类冲突
- 提供了灵活性: 支持多种部署方式和配置选项
- 优化了性能: 通过合理的类加载顺序提高启动速度
理解 Spring Boot 的类加载机制,不仅有助于排查问题,更能帮助我们写出更高效、更稳定的应用程序。
参考资料
- Spring Boot Reference Documentation
- Java Platform, Standard Edition Tools Reference
- The Java Virtual Machine Specification
本文深入探讨了 Spring Boot 的类加载机制,希望能够帮助读者更好地理解和使用 Spring Boot。如有疑问或建议,欢迎交流讨论。
相关文章:
Spring Boot 类加载机制深度解析
Spring Boot 类加载机制深度解析 前言 在 Java 应用开发中,类加载机制是一个重要且复杂的话题。Spring Boot 作为现代 Java 开发的主流框架,其类加载机制更是值得深入了解。本文将从基础概念到实际应用,全面解析 Spring Boot 的类加载机制。…...
Python 训练营打卡 Day 45
TensorBoard 简单来说,TensorBoard 是 TensorFlow 自带的一个「可视化工具」,就像给机器学习模型训练过程装了一个「监控屏幕」。你可以用它直观看到训练过程中的数据变化(比如损失值、准确率)、模型结构、数据分布等,…...

自托管图书搜索引擎Bookologia
简介 什么是 Bookologia ? Bookologia 是一个专门的书籍搜索引擎,可以在几秒钟内找到任何书籍。它是开源的,可以轻松自托管在 Docker 上,为用户提供一个简单而高效的书籍查找体验。 主要特点 简洁的用户界面:界面设计…...

前端flex、grid布局
flex布局 弹性布局是指通过调整其内元素的宽高,从而在任何的显示设备上实现对可用显示空间最佳填充的能力。弹性容器扩展其内元素来填充可用空间,或将其收缩来避免溢出 简单来说,弹性盒子模型,是为了你的网页可以在不同分辨率设…...

Maven相关问题:jna版本与ES冲突 + aop失效
文章目录 1、背景2、解决3、一点思考4、环境升级导致AOP失效5、okhttp Bean找不到6、总结 记录一些Maven依赖相关的思考 1、背景 做一个监控指标收集,用一下jna依赖: <dependency><groupId>net.java.dev.jna</groupId><artifact…...

Tomcat全方位监控实施方案指南
#作者:程宏斌 文章目录 一.二进制部署1、安装包信息2、新建配置文件2.1 配置config.yaml文件2.2 上传jar包 3、修改配置3.1 备份3.2 修改bin目录下的startup.sh文件 4、重启tomcat5、访问测试 二.docker部署1、临时方案1.1、重新启动容器1.2…...
开源PHP在线客服系统源码搭建教程
在当今数字化时代,在线客服系统已成为企业与客户沟通的重要桥梁。开源PHP客服系统因其灵活性、低成本和高可定制性而受到众多企业的青睐。本文将介绍几款优秀的开源PHP客服系统,并提供详细的搭建教程。 演示网站:gofly.v1kf.com 1.1 主流开源…...
centos7升级glibic-2.28
centos7升级glibic-2.28 最近使用trae连接服务器的时候,提示远程系统不兼容: Trae CN需要glibc 2.28或更高版本。检测到的版本: 2.17。下面是升级步骤。centos7默认的glibc不支持node v18及以上。 1、进入/home/download目录(没有download,则新建一个)…...
在Docker里面运行Docker
Docker 凭借其轻量级和可移植的容器,无疑改变了软件开发和部署的世界。但如果我告诉你 Docker 本身可以在另一个 Docker 容器中运行,你会怎么想?没错!这个概念通常被称为“Docker Inside Docker”或“DinD”,它为开发人员和系统管理员开辟了一个全新的可能性领域。在这篇博…...
设计模式复习小结
1.容易忘得设计原则 接口隔离:指接口中的功能太杂则可以拆分一下。防止实现类实现了接口后自动依赖了一些不需要的功能。不同功能拆分成不同的接口。 里氏代换:强调父类能出现的地方,子类一定能正常跑。 迪米特法则:又称最少知…...

To be or Not to be, That‘s a Token——论文阅读笔记——Beyond the 80/20 Rule和R2R
本周又在同一方向上刷到两篇文章,可以说,……同学们确实卷啊,要不卷卷开放场域的推理呢? 这两篇都在讲:如何巧妙的利用带有分支能力的token来提高推理性能或效率的。 第一篇叫 Beyond the 80/20 Rule: High-Entropy Mi…...
【基础】每天掌握一个Linux命令 - awk
目录 【基础】每天掌握一个Linux命令 - awk一、工具概述二、安装方式Ubuntu/Debian系统:CentOS/RHEL系统:macOS系统: 三、核心功能四、基础用法基本语法常用选项内置变量基本操作示例1. 打印文件所有内容2. 打印每行的第一个字段3. 指定分隔符…...

《UE5_C++多人TPS完整教程》学习笔记37 ——《P38 变量复制(Variable Replication)》
本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P38 变量复制(Variable Replication)》 的学习笔记,该系列教学视频为计算机工程师、程序员、游戏开发者、作家(Engineer, Programmer, Game Developer, Author)…...

AWS API Gateway配置日志
问题 访问API Gateway接口出现了403问题,具体报错如下: {"message":"Missing Authentication Token"}需要配置AWS API Gateway日志,看请求过程是什么样子的。 API Gateway 先找到API Gateway的的日志角色,…...

Towards Open World Object Detection概述(论文)
论文:https://arxiv.org/abs/2103.02603 代码:https://github.com/JosephKJ/OWOD Towards Open World Object Detection 迈向开放世界目标检测 Abstract 摘要 Humans have a natural instinct to identify unknown object instances in their environ…...

轻松备份和恢复 Android 系统 | 4 种解决方案
我们通常会在 Android 手机上存储大量重要的个人数据,包括照片、视频、联系人、信息等等。如果您不想丢失宝贵的数据,可以备份 Android 数据。当您需要访问和使用这些数据时,可以将其恢复到 Android 设备。如果您想了解 Android 备份和恢复&a…...

具备强大的数据处理和分析能力的智慧地产开源了
智慧地产视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。 AI是新形势下数…...
RK3588和FPGA桥片之间IO电平信号概率性不能通信原因
1.GPIO管脚配置问题 RK3588对IO进行配置的时候,如果配置为多功能复用,没有明确IO功能,可能引起信号接收不稳定, 需要在驱动中设备树中配置管脚为GPIO功能,确保没有功能复用的干扰。 2.上下拉电阻阻值设置不当 GPIO引脚…...

【iSAQB软件架构】软件架构中构建块的视图:黑箱、灰箱和白箱及其交互机制
在软件架构描述中,黑箱视图(Black-box)、灰箱视图(Gray-box)和白箱视图(White-box) 是不同抽象层级的构建模块表示方式,用于满足不同受众和设计阶段的需求。以下是基于ISAQB标准的清…...
.net jwt实现
.NET 中实现 JWT 认证:详细指南 在现代的 Web 应用开发中,安全认证是至关重要的一环。JSON Web Token(JWT)作为一种广泛使用的认证机制,为 API 提供了安全、便捷的身份验证方式。本文将详细介绍如何在 ASP.NET Core 项…...
LangChain【7】之工具创建和错误处理策略
文章目录 一 LangChain 自定义工具概述二创建自定义工具的三种方法2.1 方法一:tool 装饰器2.1.1 同步方法案例2.1.2 工具描述方式1:传参2.1.3 工具描述方式2:文档字符串 2.2 方法二:StructuredTool类2.2.1 StructuredTool创建自定…...

如何在电脑上轻松访问 iPhone 文件
我需要将 iPhone 下载文件夹中的文件传输到 Windows 11 电脑上。我该怎么做?我可以在 Windows 11 上访问 iPhone 下载吗? 由于 iOS 和 Windows 系统之间的差异,在 PC 上访问 iPhone 文件似乎颇具挑战性。然而,只要使用正确的工具…...
Eureka REST 相关接口
可供非 Java 应用程序使用的 Eureka REST 操作。 appID 是应用程序的名称,instanceID 是与实例关联的唯一标识符。在 AWS 云中,instanceID 是实例的实例 ID;在其他数据中心,它是实例的主机名。 对于 XML/JSON,HTTP 的…...

C语言字符数组输入输出方法大全(附带实例)
在 C语言中,字符数组是一种特殊的数组,用于存储和处理字符串。理解字符数组的输入和输出操作对于初学者来说至关重要,因为这是处理文本数据的基础。 字符数组的定义与初始化 在讨论输入输出之前,我们先来回顾一下字符数组的定义…...

短视频矩阵SaaS系统:开源部署与核心功能架构指南
一、系统架构概述 短视频矩阵系统是基于SaaS(软件即服务)模式的多平台内容管理解决方案,通过开源技术实现账号聚合、智能创作、跨平台分发及数据闭环。系统采用微服务架构,支持高并发场景下的弹性扩展。 二、核心功能模块开发逻辑…...
每日算法 -【Swift 算法】电话号码字母组合
🚀 LeetCode 字符串数字映射(Swift)——电话号码字母组合 在日常刷题或面试中,我们经常会遇到字符串 回溯组合的问题。这道经典题——电话号码的字母组合 就是典型代表。本文将带你用 Swift 实现这道题,思路清晰&…...
深入解析YUM与DNF:RPM包管理器的架构演进与功能对比
在Linux系统管理中,软件包管理器是连接用户与底层RPM(Red Hat Package Manager)包的核心工具。作为RPM生态的两大代表性工具,YUM(Yellowdog Updater Modified)与DNF(Dandified YUM)的…...
解决cocos 2dx/creator2.4在ios18下openURL无法调用的问题
由于ios18废弃了旧的openURL接口,我们需要修改CCApplication-ios.mm文件的Application::openURL方法: //修复openURL在ios18下无法调用的问题 bool Application::openURL(const std::string &url) {// NSString* msg [NSString stringWithCString:…...
精益数据分析(94/126):30/10/10用户参与法则与定价策略的科学制定
精益数据分析(九十四):30/10/10用户参与法则与定价策略的科学制定 在创业过程中,如何衡量用户参与度是否健康?又该如何制定科学的定价策略实现营收最大化?今天,我们将深入解析Union Square Ven…...

oss:上传图片到阿里云403 Forbidden
访问图片出现403Forbidden问题,我们可以直接登录oss账号,查看对应权限是否开通,是否存在跨域问题...