All*_*nde 290 java jar classloader
为什么在Java中这么做呢?如果您想拥有任何类型的模块系统,您需要能够动态加载jar.我被告知有一种方法可以通过编写自己的方式来完成它ClassLoader,但这对于应该(至少在我看来)像调用一个以jar文件作为参数的方法一样容易的事情来做很多工作.
这样做的简单代码的任何建议?
jod*_*ell 236
安全的原因很难.类加载器意味着不可变; 你不应该在运行时不断添加类.我真的很惊讶它与系统类加载器一起工作.以下是制作自己的子类加载器的方法:
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);
Run Code Online (Sandbox Code Playgroud)
痛苦,但它确实存在.
All*_*nde 136
以下解决方案是hackish,因为它使用反射来绕过封装,但它完美无缺:
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);
Run Code Online (Sandbox Code Playgroud)
Mar*_*nke 48
您应该看看OSGi,例如在Eclipse Platform中实现.它正是如此.您可以安装,卸载,启动和停止所谓的捆绑包,这些捆绑包实际上是JAR文件.但它做得更多,因为它提供了可以在运行时在JAR文件中动态发现的服务.
或者查看Java模块系统的规范.
Chr*_*ris 40
如何在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");
Run Code Online (Sandbox Code Playgroud)
Mor*_*hai 35
虽然这里列出的大多数解决方案要么是难以配置的黑客(JDK 9 之前)(代理),要么不再起作用(JDK 9 之后),但我发现没有人提到明确记录的方法真的很令人惊讶。
你可以创建一个自定义的系统类加载器,然后你就可以随心所欲地做任何你想做的事。不需要反射,所有类共享同一个类加载器。
启动 JVM 时添加此标志:
java -Djava.system.class.loader=com.example.MyCustomClassLoader
Run Code Online (Sandbox Code Playgroud)
类加载器必须有一个接受类加载器的构造函数,该类加载器必须设置为其父类。构造函数将在 JVM 启动时被调用并传递真正的系统类加载器,主类将由自定义加载器加载。
要添加罐子,只需调用ClassLoader.getSystemClassLoader()并将其投射到您的班级即可。
查看此实现以获取精心制作的类加载器。请注意,您可以将add()方法更改为公开。
小智 21
这是一个未弃用的版本.我修改了原始文件以删除已弃用的功能.
/**************************************************************************************************
* 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();
}
}
Run Code Online (Sandbox Code Playgroud)
fgb*_*fgb 14
使用Java 9,URLClassLoader现在的答案会产生如下错误:
java.lang.ClassCastException: java.base/jdk.internal.loader.ClassLoaders$AppClassLoader cannot be cast to java.base/java.net.URLClassLoader
Run Code Online (Sandbox Code Playgroud)
这是因为使用的类加载器已经改变.相反,要添加到系统类加载器,您可以通过代理使用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));
}
}
Run Code Online (Sandbox Code Playgroud)
添加META-INF/MANIFEST.MF并将其放在具有代理类的JAR文件中:
Manifest-Version: 1.0
Agent-Class: ClassPathAgent.ClassPathAgent
Run Code Online (Sandbox Code Playgroud)
运行代理:
这使用byte-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());
}
}
Run Code Online (Sandbox Code Playgroud)
小智 9
我发现的最好的是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());
}
Run Code Online (Sandbox Code Playgroud)
然后使用类加载器,只需:
classLoader.loadClass(name);
Run Code Online (Sandbox Code Playgroud)
如果您正在使用Android,则以下代码有效:
String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(jarFile, "/data/data/" + context.getPackageName() + "/", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");
Run Code Online (Sandbox Code Playgroud)
另一个使用 Instrumentation 的工作解决方案对我有用。它的优点是修改类加载器搜索,避免依赖类的类可见性问题:
创建代理类
对于此示例,它必须位于命令行调用的同一个 jar 上:
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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
修改MANIFEST.MF
添加对代理的引用:
Launcher-Agent-Class: agent.Agent
Agent-Class: agent.Agent
Premain-Class: agent.Agent
Run Code Online (Sandbox Code Playgroud)
我实际上使用 Netbeans,因此这篇文章有助于了解如何更改 manifest.mf
跑步
仅在 JDK 9+ 上受Launcher-Agent-Class支持,并负责加载代理,而无需在命令行上显式定义它:
java -jar <your jar>
Run Code Online (Sandbox Code Playgroud)
在 JDK 6+ 上工作的方式是定义参数-javaagent:
java -javaagent:<your jar> -jar <your jar>
Run Code Online (Sandbox Code Playgroud)
在运行时添加新的 Jar
然后,您可以根据需要使用以下命令添加 jar:
Agent.appendJarFile(new JarFile(<your file>));
Run Code Online (Sandbox Code Playgroud)
我在文档中使用它没有发现任何问题。
来自 Allain 的 hackish 解决方案的另一个版本,也适用于 JDK 11:
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});
Run Code Online (Sandbox Code Playgroud)
在 JDK 11 上,它给出了一些弃用警告,但作为在 JDK 11 上使用 Allin 解决方案的人的临时解决方案。
这是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);
}
Run Code Online (Sandbox Code Playgroud)
请注意,它依赖于特定JVM内部实现的知识,因此它不是理想的,也不是通用的解决方案。但是,如果您知道将要使用标准的OpenJDK或Oracle JVM,则这是一个快速简便的解决方法。在将来发布新的JVM版本时,它有时也可能会中断,因此您需要牢记这一点。
我知道我来晚了,但我一直在使用pf4j,它是一个插件框架,而且效果很好。
PF4J 是一个微框架,目标是保持核心简单但可扩展。
插件使用示例:
使用 ExtensionPoint 接口标记在应用程序/插件中定义扩展点:
public interface Greeting extends ExtensionPoint {
String getGreeting();
}
Run Code Online (Sandbox Code Playgroud)
使用注释创建扩展@Extension:
@Extension
public class WelcomeGreeting implements Greeting {
public String getGreeting() {
return "Welcome";
}
}
Run Code Online (Sandbox Code Playgroud)
然后您可以根据需要加载和卸载插件:
public static void main(String[] args) {
// create the plugin manager
PluginManager pluginManager = new JarPluginManager(); // or "new ZipPluginManager() / new DefaultPluginManager()"
// start and load all plugins of application
pluginManager.loadPlugins();
pluginManager.startPlugins();
// retrieve all extensions for "Greeting" extension point
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
for (Greeting greeting : greetings) {
System.out.println(">>> " + greeting.getGreeting());
}
// stop and unload all plugins
pluginManager.stopPlugins();
pluginManager.unloadPlugins();
}
Run Code Online (Sandbox Code Playgroud)
更多详细信息请参阅文档
| 归档时间: |
|
| 查看次数: |
268916 次 |
| 最近记录: |