题
为什么这么难做到这一点,在Java?如果你想拥有任何种类的模块系统,则需要能够负载罐动态。告诉我有这样做的一种方式,它通过编写自己的 ClassLoader
, 但这是一个很大的工作应该(在我的脑海里至少)是作为容易,因为调方法的一个罐子文件作为其论据。
任何建议的简单的代码做这个?
解决方案
原因很难是安全。类加载器是意味着不可改变;你不能无可奈何地添加班,以它在运行时间。实际上,我非常惊讶的是,适用的系统类装入器。这是你怎么做让你自己的孩子类装入器:
URLClassLoader child = new URLClassLoader(
new URL[] {myJar.toURI().toURL()},
this.getClass().getClassLoader()
);
Class classToLoad = Class.forName("com.MyClass", true, child);
Method method = classToLoad.getDeclaredMethod("myMethod");
Object instance = classToLoad.newInstance();
Object result = method.invoke(instance);
痛苦的,但那里它是。
其他提示
以下解决方案是予以解决了,因为它使用反射到绕过封装,但它完美的作品:
File file = ...
URL url = file.toURI().toURL();
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, url);
怎么样的 JCL类框架装载机?我不得不承认,我没有用过它,但是它看起来很有希望。
使用的例子:
JarClassLoader jcl = new JarClassLoader();
jcl.add("myjar.jar"); // Load jar file
jcl.add(new URL("http://myserver.com/myjar.jar")); // Load jar from a URL
jcl.add(new FileInputStream("myotherjar.jar")); // Load jar file from stream
jcl.add("myclassfolder/"); // Load class folder
jcl.add("myjarlib/"); // Recursively load all jar files in the folder/sub-folder(s)
JclObjectFactory factory = JclObjectFactory.getInstance();
// Create object of loaded class
Object obj = factory.create(jcl, "mypackage.MyClass");
这里是一个版本,是不是已经过时了。我修改了原来去除废弃的功能。
/**************************************************************************************************
* Copyright (c) 2004, Federal University of So Carlos *
* *
* All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without modification, are permitted *
* provided that the following conditions are met: *
* *
* * Redistributions of source code must retain the above copyright notice, this list of *
* conditions and the following disclaimer. *
* * Redistributions in binary form must reproduce the above copyright notice, this list of *
* * conditions and the following disclaimer in the documentation and/or other materials *
* * provided with the distribution. *
* * Neither the name of the Federal University of So Carlos nor the names of its *
* * contributors may be used to endorse or promote products derived from this software *
* * without specific prior written permission. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR *
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR *
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************************************/
/*
* Created on Oct 6, 2004
*/
package tools;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
/**
* Useful class for dynamically changing the classpath, adding classes during runtime.
*/
public class ClasspathHacker {
/**
* Parameters of the method to add an URL to the System classes.
*/
private static final Class<?>[] parameters = new Class[]{URL.class};
/**
* Adds a file to the classpath.
* @param s a String pointing to the file
* @throws IOException
*/
public static void addFile(String s) throws IOException {
File f = new File(s);
addFile(f);
}
/**
* Adds a file to the classpath
* @param f the file to be added
* @throws IOException
*/
public static void addFile(File f) throws IOException {
addURL(f.toURI().toURL());
}
/**
* Adds the content pointed by the URL to the classpath.
* @param u the URL pointing to the content to be added
* @throws IOException
*/
public static void addURL(URL u) throws IOException {
URLClassLoader sysloader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Class<?> sysclass = URLClassLoader.class;
try {
Method method = sysclass.getDeclaredMethod("addURL",parameters);
method.setAccessible(true);
method.invoke(sysloader,new Object[]{ u });
} catch (Throwable t) {
t.printStackTrace();
throw new IOException("Error, could not add URL to system classloader");
}
}
public static void main(String args[]) throws IOException, SecurityException, ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
addFile("C:\\dynamicloading.jar");
Constructor<?> cs = ClassLoader.getSystemClassLoader().loadClass("test.DymamicLoadingTest").getConstructor(String.class);
DymamicLoadingTest instance = (DymamicLoadingTest)cs.newInstance();
instance.test();
}
}
与 Java9, 答案与 URLClassLoader
现在给出一个错误,如:
java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader
这是因为这类机使用已经改变。相反,添加以系统舱装载机,可以使用 仪器仪表 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));
}
}
添加元INF/清单。MF,并把它放在一个罐子文件的代理类:
Manifest-Version: 1.0
Agent-Class: ClassPathAgent.ClassPathAgent
运行的剂:
这个用途的 byte-伙计-剂 库添加剂的运行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());
}
}
好的我发现是 org.apache.xbean.类装入器。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);
如果你的工作上,以下代码的工作:
String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(jarFile, "/data/data/" + context.getPackageName() + "/", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");
提出的解决方法jodonnell是好的,但应该是一点点增强。我用这个职位发展我应用程序的成功。
分配的当前线
首先,我们必须加
Thread.currentThread().setContextClassLoader(classLoader);
或者你将不能负荷资源(例如spring/context.xml)储存到罐子里。
不包括
你的瓶子进入父类装载或者你将不能够了解谁是什么。
然而,访框架仍然是最好的方式。
另一个版本的解决方案予以解决了从Allain,这也适用于JAVA11:
File file = ...
URL url = file.toURI().toURL();
URLClassLoader sysLoader = new URLClassLoader(new URL[0]);
Method sysMethod = URLClassLoader.class.getDeclaredMethod("addURL", new Class[]{URL.class});
sysMethod.setAccessible(true);
sysMethod.invoke(sysLoader, new Object[]{url});
在JAVA11它提供了一些过时的警告,但作为临时解决那些使用Allain的解决方案JAVA11.
另一个工作方案使用的仪器对我的作品。它的优点是修改类装载机的搜索,避免问题上级知名度依赖类:
创建一个代理类
对这个例子,它必须在同一罐援引的命令行:
package agent;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;
public class Agent {
public static Instrumentation instrumentation;
public static void premain(String args, Instrumentation instrumentation) {
Agent.instrumentation = instrumentation;
}
public static void agentmain(String args, Instrumentation instrumentation) {
Agent.instrumentation = instrumentation;
}
public static void appendJarFile(JarFile file) throws IOException {
if (instrumentation != null) {
instrumentation.appendToSystemClassLoaderSearch(file);
}
}
}
修改清单。MF
增加的参考剂:
Launcher-Agent-Class: agent.Agent
Agent-Class: agent.Agent
Premain-Class: agent.Agent
我实际上使用//需要将物品寄,所以 这个职位 可以帮助对如何改变的清单。mf
运行
的 Launcher-Agent-Class
只支持在JAVA9+和负责装卸代理没有明确界定它的命令行:
java -jar <your jar>
的方式,适用于JAVA6+定义 -javaagent
参数:
java -javaagent:<your jar> -jar <your jar>
增加新的罐子在运行
然后你可以增加罐必要时使用以下命令:
Agent.appendJarFile(new JarFile(<your file>));
我没有找到任何问题,使用这个文件。
这里是一个快速的解决方法Allain的方法,使其符合新版本的Java:
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try {
Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, new File(jarPath).toURI().toURL());
} catch (NoSuchMethodException e) {
Method method = classLoader.getClass()
.getDeclaredMethod("appendToClassPathForInstrumentation", String.class);
method.setAccessible(true);
method.invoke(classLoader, jarPath);
}
注意,它依赖于知识的内部执行的具体JVM,所以它不是理想的和这不是一个普遍适用的解决办法。但它是一个快速和容易的解决方法如果你知道你打算使用的标准们可根据最新的技术提供一些或Oracle JVM。它也可能打破在某一点在未来,当新的JVM版本发布,所以你需要记住这一点。
这可以是一个反应迟缓,我可以做它作为这个(一个简单的例子fastutil-8.2.2.jar)使用jhplot.网络课程从DataMelt(http://jwork.org/dmelt)
import jhplot.Web;
Web.load("http://central.maven.org/maven2/it/unimi/dsi/fastutil/8.2.2/fastutil-8.2.2.jar"); // now you can start using this library
根据该文件,此文件将下载里面的"lib/用户"和后来的动态加载,因此可以立即开始使用类从这个罐子的文件在同一程序。
请看看这个项目,我开始: 代理目lib
这lib将载罐子文件系统或任何其他位置。它将致力于类装入器的罐,以确保没有任何库的冲突。用户将能够创造任何对象的自载罐子里并呼吁任何方法。这lib的目的是要载罐编Java8从码的基础,支持Java7.
创造一个对象:
File libDir = new File("path/to/jar");
ProxyCallerInterface caller = ObjectBuilder.builder()
.setClassName("net.proxy.lib.test.LibClass")
.setArtifact(DirArtifact.builder()
.withClazz(ObjectBuilderTest.class)
.withVersionInfo(newVersionInfo(libDir))
.build())
.build();
String version = caller.call("getLibVersion").asString();
ObjectBuilder支持工厂的方法,呼吁静态功能,并回电话接口实现的。我将张贴更多的例子上的自述页。
我个人觉得 java。工具.ServiceLoader 不会的工作得很好。你可以得到的一个例子 在这里,.