为什么在Java中这么难做到这一点?如果您希望拥有任何类型的模块系统,您需要能够动态加载JAR文件。有人告诉我,有一种方法可以通过编写自己的ClassLoader来实现,但这需要做很多工作,因为(至少在我看来)应该像调用以JAR文件作为参数的方法一样简单。

对实现这一功能的简单代码有什么建议吗?


当前回答

在Java 9中,URLClassLoader的答案现在会给出如下错误:

java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader

这是因为所使用的类装入器发生了变化。相反,要添加到系统类装入器,您可以通过代理使用Instrumentation API。

创建代理类:

package ClassPathAgent;

import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;

public class ClassPathAgent {
    public static void agentmain(String args, Instrumentation instrumentation) throws IOException {
        instrumentation.appendToSystemClassLoaderSearch(new JarFile(args));
    }
}

添加meta - inf / MANIFEST。MF并将其与代理类一起放在JAR文件中:

Manifest-Version: 1.0
Agent-Class: ClassPathAgent.ClassPathAgent

运行代理:

使用bytes -buddy-agent库将代理添加到运行的JVM中:

import java.io.File;

import net.bytebuddy.agent.ByteBuddyAgent;

public class ClassPathUtil {
    private static File AGENT_JAR = new File("/path/to/agent.jar");

    public static void addJarToClassPath(File jarFile) {
        ByteBuddyAgent.attach(AGENT_JAR, String.valueOf(ProcessHandle.current().pid()), jarFile.getPath());
    }
}

其他回答

你应该看一看OSGi,比如在Eclipse平台中实现的。它就是这么做的。您可以安装、卸载、启动和停止所谓的包,这些包实际上是JAR文件。但是它做的更多,例如,它提供了可以在运行时在JAR文件中动态发现的服务。

或参阅Java模块系统的规范。

我个人认为java.util.ServiceLoader做得很好。你可以在这里得到一个例子。

我找到的最好的是org.apache.xbean.classloader.JarFileClassLoader,它是XBean项目的一部分。

下面是我过去使用的一个简短方法,用于从特定目录中的所有lib文件创建一个类装入器

public void initialize(String libDir) throws Exception {
    File dependencyDirectory = new File(libDir);
    File[] files = dependencyDirectory.listFiles();
    ArrayList<URL> urls = new ArrayList<URL>();
    for (int i = 0; i < files.length; i++) {
        if (files[i].getName().endsWith(".jar")) {
        urls.add(files[i].toURL());
        //urls.add(files[i].toURI().toURL());
        }
    }
    classLoader = new JarFileClassLoader("Scheduler CL" + System.currentTimeMillis(), 
        urls.toArray(new URL[urls.size()]), 
        GFClassLoader.class.getClassLoader());
}

然后使用类加载器,只需执行:

classLoader.loadClass(name);

如果你在Android上工作,下面的代码可以工作:

String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(jarFile, "/data/data/" + context.getPackageName() + "/", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");

对于jar文件的动态上传,您可以使用我对URLClassLoader的修改。这种修改对于在应用程序操作期间更改jar文件没有问题,就像标准的URLClassloader一样。所有加载的jar文件都加载到RAM中,因此独立于原始文件。

内存中的jar和JDBC类装入器