Spring Boot本地类和Jar包类加载顺序深度剖析

本文将从多个方面对Spring Boot本地类和Jar包类加载顺序做详细的阐述,并给出相应的代码示例。

一、类加载机制概述

在介绍Spring Boot本地类和Jar包类加载顺序之前,有必要简要介绍一下Java的类加载机制。

Java虚拟机在程序运行期间动态加载类,并将类的字节码转换成运行时数据结构,形成可执行代码。类的加载过程由ClassLoader类及其子类完成。

Java虚拟机规范将ClassLoader类设计成了一个抽象类,用以描述Java虚拟机中存在的类加载器,用以加载Class文件。ClassLoader类的任务是把class文件字节码转化为java.lang.Class的一个实例。而Class实例通常存放于运行时数据区的方法区内。

二、本地类和Jar包类加载顺序详解

2.1 Jar包优先于本地类

在Java类加载过程中,如果系统中存在多个相同的类文件,则优先加载JVM类路径下的Jar包中的class文件,而忽略本地类路径下的同名class文件。

为了说明这一点,我们可以自己手写一个同名class文件,并同时存放在JVM类路径下和本地类路径下。接着,我们在代码中进行加载并打印该class文件的内容。

// 在JVM类路径下创建SameClass.java
package com.example.demo;

public class SameClass {
    public static void printFile() {
        System.out.println("This is a file in JVM classpath.");
    }
}
// 在本地类路径下创建SameClass.java,内容和上述相同
package com.example.demo;

public class SameClass {
    public static void printFile() {
        System.out.println("This is a file in local classpath.");
    }
}
// 在代码中进行加载并打印SameClass类文件的内容
package com.example.demo;

public class ClassLoadingOrderDemoApplication {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader = ClassLoadingOrderDemoApplication.class.getClassLoader();
        Class clazz = classLoader.loadClass("com.example.demo.SameClass");
        clazz.getMethod("printFile").invoke(null, null);
    }
}

运行上面的代码之后,我们发现输出结果是”This is a file in JVM classpath.”。

这是因为JVM类路径下的SameClass.class文件优先被加载,而本地类路径下的同名文件被忽略了。

2.2 本地类优先于系统类

对于Jar包中不存在的类文件,Java会首先从本地文件系统中查找,然后才会查找系统类库(如JDK中的rt.jar)。

同样地,我们可以手写一个只在本地类路径下存在的class文件,并在代码中进行加载和打印。

// 在本地类路径下创建OnlyLocalClass.java
package com.example.demo;

public class OnlyLocalClass {
    public static void printFile() {
        System.out.println("This is a file in local classpath, but not in the system classpath.");
    }
}
// 在代码中进行加载并打印OnlyLocalClass类文件的内容
package com.example.demo;

public class ClassLoadingOrderDemoApplication {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader = ClassLoadingOrderDemoApplication.class.getClassLoader();
        Class clazz = classLoader.loadClass("com.example.demo.OnlyLocalClass");
        clazz.getMethod("printFile").invoke(null, null);
    }
}

运行上述代码之后,我们可以发现打印结果为”This is a file in local classpath, but not in the system classpath.”。

由此可见,本地类优先于系统类。

2.3 父ClassLoader优先于子ClassLoader

对于同一份class文件,父ClassLoader的加载顺序优先于子ClassLoader。

我们可以通过自定义ClassLoader来模拟这种情况,并在代码中进行加载和打印。

// 自定义ClassLoader
package com.example.demo;

import java.io.IOException;
import java.io.InputStream;

public class MyClassLoader extends ClassLoader {
    @Override
    protected Class findClass(String name) throws ClassNotFoundException {
        try {
            String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
            InputStream is = this.getClass().getResourceAsStream(fileName);
            if (is == null) {
                return super.loadClass(name);
            }
            byte[] b = new byte[is.available()];
            is.read(b);
            return defineClass(name, b, 0, b.length);
        } catch (IOException e) {
            throw new ClassNotFoundException(name);
        }
    }
}
// 在本地类路径下创建ParentClass.java
package com.example.demo;

public class ParentClass {
    public static void printFile() {
        System.out.println("This is a file in parent classloader.");
    }
}
// 在代码中进行加载并打印ParentClass类文件的内容
package com.example.demo;

public class ClassLoadingOrderDemoApplication {
    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader parentClassLoader = new MyClassLoader();
        ClassLoader childClassLoader = new MyClassLoader();
        
        parentClassLoader.loadClass("com.example.demo.ParentClass");
        Class clazz = childClassLoader.loadClass("com.example.demo.ParentClass");
        clazz.getMethod("printFile").invoke(null, null);
    }
}

运行上述代码之后,我们可以发现打印结果为”This is a file in parent classloader.”,即父ClassLoader先于子ClassLoader加载了该class文件。

三、小结

本文从类加载机制概述、Jar包与本地类加载顺序、父ClassLoader与子ClassLoader的加载顺序等多个方面,对Spring Boot本地类和Jar包类加载顺序做了详细剖析。

Java虚拟机能够动态加载类,并将类的字节码转换成运行时数据结构形成可执行代码,类的加载过程由ClassLoader类及其子类完成。

原创文章,作者:OISGV,如若转载,请注明出处:https://www.506064.com/n/373731.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
OISGVOISGV
上一篇 2025-04-27 15:26
下一篇 2025-04-27 15:26

相关推荐

  • Java Bean加载过程

    Java Bean加载过程涉及到类加载器、反射机制和Java虚拟机的执行过程。在本文中,将从这三个方面详细阐述Java Bean加载的过程。 一、类加载器 类加载器是Java虚拟机…

    编程 2025-04-29
  • QML 动态加载实践

    探讨 QML 框架下动态加载实现的方法和技巧。 一、实现动态加载的方法 QML 支持从 JavaScript 中动态指定需要加载的 QML 组件,并放置到运行时指定的位置。这种技术…

    编程 2025-04-29
  • Spring Boot 集成 Jacoco

    本文将从以下几个方面介绍如何在 Spring Boot 中集成 Jacoco:1、Jacoco 概述;2、Spring Boot 集成 Jacoco 的配置;3、生成 Jacoco…

    编程 2025-04-29
  • Spring Boot中发GET请求参数的处理

    本文将详细介绍如何在Spring Boot中处理GET请求参数,并给出完整的代码示例。 一、Spring Boot的GET请求参数基础 在Spring Boot中,处理GET请求参…

    编程 2025-04-29
  • 深度查询宴会的文化起源

    深度查询宴会,是指通过对一种文化或主题的深度挖掘和探究,为参与者提供一次全方位的、深度体验式的文化品尝和交流活动。本文将从多个方面探讨深度查询宴会的文化起源。 一、宴会文化的起源 …

    编程 2025-04-29
  • 如何在Spring Cloud中整合腾讯云TSF

    本篇文章将介绍如何在Spring Cloud中整合腾讯云TSF,并提供完整的代码示例。 一、TSF简介 TSF (Tencent Serverless Framework)是腾讯云…

    编程 2025-04-29
  • 如何使用Spring Boot ElasticJob进行配置覆盖

    本文将详细介绍如何使用Spring Boot ElasticJob进行配置覆盖。 一、目录结构 我们需要准备两个目录,分别是“elastic-job-lite-spring-boo…

    编程 2025-04-28
  • Spring Boot中使用DTO、Controller、Service、Mapper进行开发

    本文将介绍如何在Spring Boot中使用DTO、Controller、Service、Mapper等技术进行开发。 一、DTO DTO(Data Transfer Object…

    编程 2025-04-28
  • Spring S_CSRF防护机制实现及应用

    Spring S_CSRF防护机制是Spring Security框架提供的一个针对跨站请求伪造攻击(CSRF)的保护机制。本文将从以下几个方面详细介绍Spring S_CSRF防…

    编程 2025-04-28
  • Python下载深度解析

    Python作为一种强大的编程语言,在各种应用场景中都得到了广泛的应用。Python的安装和下载是使用Python的第一步,对这个过程的深入了解和掌握能够为使用Python提供更加…

    编程 2025-04-28

发表回复

登录后才能评论