当前位置: 代码网 > it编程>编程语言>Java > Spring Boot 类加载流程分析

Spring Boot 类加载流程分析

2026年03月11日 Java 我要评论
java 类加载基础类加载器层次结构java 虚拟机使用双亲委派模型(parent delegation model)来加载类:bootstrap classloader (启动类加载器) ↓e

java 类加载基础

类加载器层次结构

java 虚拟机使用双亲委派模型(parent delegation model)来加载类:

bootstrap classloader (启动类加载器)
    ↓
extension/platform classloader (扩展/平台类加载器)
    ↓
application/system classloader (应用/系统类加载器)

双亲委派机制

双亲委派模型的工作原理:

  1. 当一个类加载器收到类加载请求时,它首先不会自己去尝试加载这个类
  2. 而是把这个请求委派给父类加载器去完成
  3. 每一个层次的类加载器都是如此
  4. 只有当父加载器反馈自己无法完成这个加载请求(找不到所需的类)时,子加载器才会尝试自己去加载
protected class<?> loadclass(string name, boolean resolve) {
    synchronized (getclassloadinglock(name)) {
        // 1. 检查是否已经加载
        class<?> c = findloadedclass(name);
        if (c == null) {
            try {
                if (parent != null) {
                    // 2. 委派给父类加载器
                    c = parent.loadclass(name, false);
                } else {
                    c = findbootstrapclassornull(name);
                }
            } catch (classnotfoundexception e) {
                // 父类加载器无法加载
            }
            if (c == null) {
                // 3. 自己尝试加载
                c = findclass(name);
            }
        }
        if (resolve) {
            resolveclass(c);
        }
        return c;
    }
}

spring boot 类加载架构

为什么需要特殊的类加载器?

spring boot 应用通常打包为可执行的 jar(fat jar 或 uber jar),这种 jar 包含了:

  • 应用代码
  • 所有依赖的第三方库
  • spring boot 自身的类

传统的 jar 加载机制无法正确处理这种嵌套的 jar 结构,因此 spring boot 实现了自定义的类加载器。

spring boot 启动流程

java -jar app.jar
    ↓
jarlauncher.main()
    ↓
创建 launchedurlclassloader
    ↓
设置 contextclassloader
    ↓
调用应用程序的 main 方法

核心类

spring boot 类加载相关的核心类位于 org.springframework.boot.loader 包:

类名作用
jarlauncher可执行 jar 的启动器
warlauncher可执行 war 的启动器
launchedurlclassloaderspring boot 自定义的类加载器
jarfile封装 jar 文件访问
jarfileregisterjar 文件注册表

fat jar 结构

目录结构

spring boot 可执行 jar 的内部结构:

app.jar
├── boot-inf/
│   ├── classes/           # 应用类文件
│   │   └── com/example/
│   │       └── application.class
│   └── lib/               # 依赖库
│       ├── spring-boot-2.7.0.jar
│       ├── spring-core-5.3.20.jar
│       └── ...
├── meta-inf/
│   ├── manifest.mf        # 清单文件
│   └── spring.factories   # spring 配置
├── org/springframework/boot/loader/  # spring boot loader 类
│   ├── jarlauncher.class
│   ├── launchedurlclassloader.class
│   └── ...
└── [其他资源]

manifest.mf 关键配置

manifest-version: 1.0
start-class: com.example.application
main-class: org.springframework.boot.loader.jarlauncher
spring-boot-classes: boot-inf/classes/
spring-boot-lib: boot-inf/lib/
spring-boot-version: 2.7.0

关键属性说明:

  • main-class: 指向 spring boot 的启动器类
  • start-class: 实际的应用程序主类
  • spring-boot-classes: 应用类所在目录
  • spring-boot-lib: 依赖库所在目录

launchedurlclassloader 详解

继承关系

java.lang.classloader
    ↓
java.net.urlclassloader
    ↓
org.springframework.boot.loader.launchedurlclassloader

核心特性

1. 打破双亲委派

launchedurlclassloader 在某些情况下会打破传统的双亲委派模型:

@override
protected class<?> loadclass(string name, boolean resolve) throws classnotfoundexception {
    // 1. 检查是否已经加载
    class<?> loadedclass = findloadedclass(name);
    if (loadedclass == null) {
        // 2. 对于某些包,优先从 boot-inf 加载
        if (iseligibleforoverride(name)) {
            try {
                loadedclass = findclass(name);
                if (resolve) {
                    resolveclass(loadedclass);
                }
                return loadedclass;
            } catch (classnotfoundexception e) {
                // 继续尝试父加载器
            }
        }
        // 3. 使用标准的双亲委派
        return super.loadclass(name, resolve);
    }
    return loadedclass;
}

2. url 处理

launchedurlclassloader 能够处理嵌套 jar 的 url:

jar:file:/path/to/app.jar!/boot-inf/lib/spring-core-5.3.20.jar!/

这种 url 格式表示:

  • 外层 jar: file:/path/to/app.jar
  • 内层 jar: boot-inf/lib/spring-core-5.3.20.jar

3. 资源加载

@override
public url findresource(string name) {
    // 优先从 boot-inf/classes 查找
    url url = findresourceinbootinf(name);
    if (url != null) {
        return url;
    }
    // 然后从 boot-inf/lib 查找
    return findresourceindependencies(name);
}

类加载优先级

spring boot 类加载器的加载优先级:

bootstrap classloader (jdk 核心类)
launchedurlclassloader (boot-inf/classes)
launchedurlclassloader (boot-inf/lib)
extension/platform classloader
application classloader (其他)

类加载委托模型

spring boot 的委托策略

spring boot 并非完全打破双亲委派,而是在特定场景下进行调整:

正常情况(遵循双亲委派):
application classloader → launchedurlclassloader → bootstrap classloader
特殊情况(打破双亲委派):
对于 spring boot 应用类,launchedurlclassloader 优先加载

加载决策流程

收到类加载请求
    ↓
是否是 jdk 核心类?
    ↓ 是 → bootstrap classloader 加载
    ↓ 否
是否是 spring boot loader 类?
    ↓ 是 → bootstrap classloader 加载
    ↓ 否
是否在 boot-inf/classes 中?
    ↓ 是 → launchedurlclassloader 加载
    ↓ 否
是否在 boot-inf/lib 中?
    ↓ 是 → launchedurlclassloader 加载
    ↓ 否
委托给父类加载器

类隔离

spring boot 的类加载机制实现了类隔离:

  • 应用类boot-inf/classes/
  • 依赖类boot-inf/lib/
  • 系统类rt.jar, jce.jar

这种隔离确保了:

  1. 应用类不会被系统类覆盖
  2. 不同版本的依赖可以共存
  3. 类加载冲突最小化

常见问题与解决方案

1. classnotfoundexception

问题现象:

java.lang.classnotfoundexception: com.example.myclass

可能原因:

  • 类文件不在 boot-inf/classes/
  • 依赖 jar 不在 boot-inf/lib/
  • manifest.mf 配置错误

解决方案:

<!-- maven 确保正确打包 -->
<plugin>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-maven-plugin</artifactid>
</plugin>

2. noclassdeffounderror

问题现象:

java.lang.noclassdeffounderror: org/springframework/core/resolvabletypeprovider

可能原因:

  • 依赖版本冲突
  • 类加载顺序问题
  • 传递依赖未正确包含

解决方案:

<!-- 排除冲突依赖 -->
<dependency>
    <groupid>com.example</groupid>
    <artifactid>some-lib</artifactid>
    <exclusions>
        <exclusion>
            <groupid>org.springframework</groupid>
            <artifactid>spring-core</artifactid>
        </exclusion>
    </exclusions>
</dependency>

3. classcastexception

问题现象:

java.lang.classcastexception: com.example.myclass cannot be cast to com.example.myclass

可能原因:

  • 同一个类被不同的类加载器加载
  • 类加载器隔离导致类型不匹配

解决方案:

// 确保使用正确的类加载器
classloader classloader = thread.currentthread().getcontextclassloader();
class<?> clazz = classloader.loadclass("com.example.myclass");

4. nosuchmethoderror

问题现象:

java.lang.nosuchmethoderror: org.springframework.util.stringutils.isempty(ljava/lang/string;)z

可能原因:

  • 依赖版本不匹配
  • 方法签名在不同版本中发生变化

解决方案:

<!-- 使用 dependencymanagement 统一版本 -->
<dependencymanagement>
    <dependencies>
        <dependency>
            <groupid>org.springframework</groupid>
            <artifactid>spring-framework-bom</artifactid>
            <version>5.3.20</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencymanagement>

5. 资源文件找不到

问题现象:

inputstream is = getclass().getresourceasstream("/config.properties"); // 返回 null

可能原因:

  • 资源文件打包位置不正确
  • 使用了错误的类加载器

解决方案:

// 使用正确的加载方式
inputstream is = thread.currentthread()
    .getcontextclassloader()
    .getresourceasstream("config.properties");
// 或者使用 classpathresource
resource resource = new classpathresource("config.properties");
inputstream is = resource.getinputstream();

最佳实践

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>

2. 避免类冲突

<!-- 排除不需要的传递依赖 -->
<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-web</artifactid>
    <exclusions>
        <exclusion>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-tomcat</artifactid>
        </exclusion>
    </exclusions>
</dependency>

3. 自定义类加载器

public class customclassloader extends launchedurlclassloader {
    @override
    protected class<?> loadclass(string name, boolean resolve) 
            throws classnotfoundexception {
        // 自定义加载逻辑
        if (name.startswith("com.custom.")) {
            class<?> clazz = findloadedclass(name);
            if (clazz == null) {
                clazz = findclass(name);
            }
            if (resolve) {
                resolveclass(clazz);
            }
            return clazz;
        }
        return super.loadclass(name, resolve);
    }
}

4. 资源加载最佳实践

@service
public class resourceservice {
    // 推荐:使用 resource 抽象
    @value("classpath:config.properties")
    private resource configresource;
    public void loadconfig() throws ioexception {
        properties props = new properties();
        try (inputstream is = configresource.getinputstream()) {
            props.load(is);
        }
    }
    // 推荐:使用 classpathresource
    public resource loadresource(string path) {
        return new classpathresource(path);
    }
}

5. 调试类加载问题

@component
public class classloaderdebugger implements applicationlistener<applicationreadyevent> {
    @override
    public void onapplicationevent(applicationreadyevent event) {
        classloader classloader = thread.currentthread().getcontextclassloader();
        system.out.println("context classloader: " + classloader);
        system.out.println("parent classloader: " + classloader.getparent());
        // 打印类路径
        if (classloader instanceof urlclassloader) {
            url[] urls = ((urlclassloader) classloader).geturls();
            system.out.println("classpath urls:");
            for (url url : urls) {
                system.out.println("  " + url);
            }
        }
    }
}

6. jvm 参数配置

# 启用类加载详细日志
java -verbose:class -jar app.jar
# 设置类加载器调试
java -djava.security.debug=classloader -jar app.jar
# 指定自定义类加载器
java -djava.system.class.loader=com.example.customclassloader -jar app.jar

总结

spring boot 的类加载机制是其可执行 jar 功能的核心:

  1. launchedurlclassloader 是核心类加载器,能够处理嵌套 jar 结构
  2. fat jar 结构 将应用类和依赖类组织在 boot-inf 目录下
  3. 类加载策略 在保持双亲委派的同时,针对 spring boot 应用进行了优化
  4. 类隔离 确保了应用类和依赖类的正确加载顺序

理解 spring boot 的类加载机制有助于:

  • 解决类加载相关的各种问题
  • 优化应用启动性能
  • 实现更复杂的应用架构
  • 进行有效的故障排查

到此这篇关于spring boot 类加载流程分析的文章就介绍到这了,更多相关springboot类加载内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com