java运行时加载jar包

本文介绍了一种在Java运行时从内存中加载并执行jar包的方法,解决了因文件系统限制导致无法直接加载的问题。通过自定义ClassLoader,将jar包内容读取到内存,并动态加载指定类执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java运行时加载jar包

一、故事背景(发疯言论,可跳过)

有一个调度平台s,你可以上传jar包在平台调度执行
一直以来,均由你一个人负责开发和上传demo.jar,你感觉一切都还不错
在这里插入图片描述

但是有一天,突然来了两个同学要和你一起开发维护demo.jar
很多业务并行起来,瞬间拉出三个分支,这让你感到焦虑
在这里插入图片描述
于是你想让demo接入一个项目管理平台p,对所有开发分支自动合并打包,而不再是本地打包上传的模式

但是平台p和s无法直接打通,p在编译打包完成后无法将demo.jar自动上传平台s
这就很恼火了,你还得下载demo.jar,再去平台s上传
但还有更可恶的事情,平台s还只允许一个owner上传jar包
换句话说,别人的所有变更都要你来参与这个jar包的下载和上传
在这里插入图片描述

于是我决定在平台s上重新上传一个稳定不变的loader.jar
这里面不再直接写业务逻辑,而是去下载平台p打包好的demo.jar然后执行它

于是有了第一次尝试:下载后使用UrlCassLoader实现运行时加载执行jar包(有很多文章已经讲过如何使用它了,这里便不再赘述)
但是作为程序员的你一定料到了,事情不可能这么顺利
当你在本地测试一切顺利后,把loader.jar上传到平台s
exception: file system is readonly
哈哈,不让你下载文件到本地

那为什么一定要写到文件再加载呢,有没有办法直接在内存搞定?
那就让我们来试试吧

二、生成demo.jar

我们创建一个Module:jar-demo
然后简单写一个hello world
在这里插入图片描述

package org.example;

public class DemoEntry {
    public void entry() {
        System.out.println("hello world");
    }
}

然后 mvn package 生成jar包
在这里插入图片描述

三、把jar包加载到内存,创建自定义classLoader

package org.example.loader;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.SecureClassLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class StreamLoadDemo {

    public static void main(String[] args) throws Exception {

        //这里还是用我们刚刚打包的本地jar包创建输入流,对于网络下载文件可以替换对应的inputStream
        String jarPath = System.getProperty("user.dir") + "/jar-demo/target/jar-demo-1.0-SNAPSHOT.jar";
        JarInputStream jarInputStream = new JarInputStream(Files.newInputStream(Paths.get(jarPath)));

		//类名->字节码索引
        Map<String, int[]> classByteMap = new HashMap<>();
        byte[] buffer = new byte[1024 * 1024];
        int head = 0;
        int chunk = 256;

        JarEntry jarEntry = jarInputStream.getNextJarEntry();
        while (jarEntry != null) {
            String name = jarEntry.getName();
            if (name.endsWith(".class")) {
                String className = name.replaceAll("/", ".").replaceAll(".class", "");
                int size = 0, read;
                while (true) {
                    read = jarInputStream.read(buffer, head + size, chunk);
                    if (read == -1) {
                        break;
                    }
                    size += read;
                }
                classByteMap.put(className, new int[]{head, size});
                head += size;
            }
            jarEntry = jarInputStream.getNextJarEntry();
        }

        SecureClassLoader classLoader = new SecureClassLoader() {
            @Override
            protected Class<?> findClass(String name) {
                int[] arr = classByteMap.get(name);
                return super.defineClass(name, buffer, arr[0], arr[1]);
            }
        };

        Class<?> clazz = classLoader.loadClass("org.example.DemoEntry");
        Constructor<?> constructor = clazz.getConstructor();
        Object demoEntry = constructor.newInstance();
        Method entry = clazz.getDeclaredMethod("entry");
        entry.invoke(demoEntry);
    }
}

运行结果:
在这里插入图片描述
功能实现了,不过我也是知其然不知其所以然,要是大家对于java类加载有更多的了解,对这个功能有更优雅的实现,欢迎在评论区留言~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值