[英]Java: Adding classes from external source to classpath
所以这是我的问题。 我正在创建一个类似于DOS命令行的软件。 我希望人们能够通过简单地删除包含将基本Command
类扩展到文件夹的类的jar文件来添加其他命令。 我没有尝试过任何方法。
这是我尝试过的:
SomeClass$1.class
事情有关。 ZipEntry
的迭代转换为可以添加到类路径的任何内容,例如URL。 我希望在这里有所帮助。 任何有关如何使我的程序更具扩展性的建议都将受到欢迎。 +1包括代码;)
如果您需要更多信息,请告诉我。
编辑:
新代码:
URLClassLoader ucl = (URLClassLoader) ClassLoader.getSystemClassLoader();
for(File file : FileHelper.getProtectedFile("/packages/").listFiles()) {
if(file.getName().endsWith(".jar")) {
Set<Class<?>> set = new HashSet<Class<?>>();
JarFile jar = null;
try {
jar = new JarFile(file);
Enumeration<JarEntry> entries = jar.entries();
while(entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if(!entry.getName().endsWith(".class"))
continue;
Class<?> clazz;
try {
clazz = ucl.loadClass(entry.getName().replace("/", ".").replace(".class", "")); // THIS IS LINE 71
} catch(ClassNotFoundException e) {
e.printStackTrace();
continue;
}
if(entry.getName().contains("$1"))
continue;
if(clazz.getName().startsWith("CMD_"))
set.add(clazz);
jar.close();
}
} catch(Exception e) {
//Ignore file
try {
jar.close();
} catch (IOException e1) {/* Don't worry about it too much... */}
OutputHelper.log("An error occurred whilst adding package " + OutputStyle.ITALIC + file.getName() + OutputStyle.DEFAULT + ". " + e.getMessage() + ". Deleting package...", error);
e.printStackTrace();
file.delete();
}
引发错误:
java.lang.ClassNotFoundException: com.example.MyCommands.CMD_eggtimer
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at com.MyWebsite.MyApplication.Commands.CommandList.update(CommandList.java:71)
上面的代码中已标记第71行。
过去需要这样做时,我结合使用了第2点和第3点,即我将jar文件添加到类路径中,然后对其进行遍历,列出其中包含的文件以计算出类名,并使用Class.forName(...)加载它们,这样我就可以检查它们是否是需要对其进行处理的类。
另一个选择是要求在jar文件的清单中或通过其他一些元数据机制来标识这些类。
谢谢大家的帮助,但我自己解决了。
首先,您需要要处理的文件的位置。 使用它来创建jar的File和JarFile。
File file = new File("/location-to/file.jar");
JarFile jar = new JarFile(file);
如果需要,可以通过遍历文件夹中的内容来完成此操作。
然后,您需要为该文件创建URLClassLoader
:
URLClassLoader ucl = new URLClassLoader(new URL[] {file.toURI().toURL()});
然后遍历Jar文件中的所有类。
Enumeration<JarEntry> entries = jar.entries();
while(entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if(!entry.getName().endsWith(".class"))
continue;
Class<?> clazz;
try {
clazz = ucl.loadClass(entry.getName().replace("/", ".").replace(".class", ""));
} catch(ClassNotFoundException e) {
e.printStackTrace();
continue;
}
if(entry.getName().contains("$1"))
continue;
if(clazz.getSimpleName().startsWith("CMD_"))
set.add(clazz); //Sort the classes how you like. Here I added ones beginning with 'CMD_' to a HashSet for later manipulation
不要忘记关闭内容:
jar.close();
ucl.close();
因此,让我们回顾一下发生的事情:
URLClassLoader
将其添加到类路径中 URLClassLoader
和JarFile
以防止内存泄漏 晕!
扫描类路径以查找实现给定接口的所有类似乎很麻烦,但是似乎有一些方法可以这样做-那里的答案建议使用Reflections库(前提是我实际上从未使用过)
一种更明智的方法,即使它暗示了一些元数据编辑,也将使用ServiceLoader机制。
编辑 :我错过了您的帖子中的Reflections参考,对不起...仍然,关于ServiceLoader的建议仍然适用。
如果您使用类路径中的jar启动程序,则可以通过获取系统属性java.class.path
并创建一个JarFile
对象来获取该jar路径。 然后,您可以遍历该对象的entry 。 每个条目都有一个路径(相对于jar)。 您可以解析此路径以将包和类名获取为com.package.Class
。 然后,您可以使用ClassLoader
加载类并对其进行任何操作。 (您可能想使用另一个ClassLoader,但是它可以与下面的类一起使用)
public class Main {
static ClassLoader clazzLoader = Main.class.getClassLoader();
public static void main(String [] args) {
try {
List<Class<?>> classes = new LinkedList<>();
// do whatever transformation you need to get the jar file
String classpath = System.getProperty("java.class.path").split(";")[1];
// I'm just checking if it's the right jar file
System.out.println(classpath);
// get the file and make a JarFile out of it
File bin = new File(classpath);
JarFile jar = new JarFile(bin);
// get all entries in the jar file
Enumeration<JarEntry> entries = jar.entries();
// iterate over jarfile entries
while(entries.hasMoreElements()) {
try {
JarEntry entry = entries.nextElement();
// skip other files in the jar
if (!entry.getName().endsWith(".class"))
continue;
// extract the class name from its URL style name
String className = entry.getName().replace("/", ".").replace(".class", "");
// load the class
Class<?> clazz = clazzLoader.loadClass(className);
// use your Command class or any other class you want to match
if (Comparable.class.isAssignableFrom(clazz))
classes.add(clazz);
} catch (ClassNotFoundException e) {
//ignore
}
}
for (Class<?> clazz : classes) {
System.out.println(clazz);
clazz.newInstance(); // or whatever
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
我已经使用Inner类进行了尝试,它也可以正常工作,但我不知道Reflections
库的解析方式。
另外,您不需要一个完整的Jar即可执行此操作。 如果您的bin文件夹中带有.class
文件,则可以递归地浏览其文件/文件夹,将类文件解析为正确的格式,然后像上面一样加载它们。
编辑您的新代码,几件事
jar.close()
。 if(clazz.getName().startsWith("CMD_"))
没有做您想要的事情。 getName()
方法返回类的完整路径名,包括包。 使用getSimpleName()
如果您收到ClassNotFoundException
,那是因为该类不在类路径中。 这是告诉我您没有在类路径上运行带有jar文件的程序。 加载类时,仅打开zip / jar文件并“加载”类文件是不够的。 你必须像这样运行程序
java -classpath <your_jars> com.your.class.Main
或使用Eclipse(或其他IDE),将jar添加到您的构建路径中。
查看此内容,以获取有关使用类路径条目运行Java的更多说明。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.