我正在运行一个可执行jar并希望在jar中找到一个类列表,以便我可以在运行时决定运行哪个。我可能不知道jar文件的名称,因此无法解压缩
答案 0 :(得分:1)
我不确定是否有办法列出当前类加载器可见的所有类。
缺乏这一点,你可以
a)尝试从类路径中找出jar文件的名称,然后查看其内容。
或
b)假设您有一个您正在寻找的候选类列表,请使用Class.forName()尝试每个类。
答案 1 :(得分:1)
您无法使用Reflection API从jar包中枚举类。在相关问题how-can-i-enumerate-all-classes-in-a-package和{}中也明确说明了这一点 can-i-list-the-resources-in-a-given-package。我曾经写过一个工具,列出了在某个类路径中找到的所有类。粘贴这里太长了,但这是一般方法:
找到使用过的类路径。 eirikma在另一个答案中很好地展示了这一点。
添加ClassLoader可能搜索类的其他地方,例如引导类路径,JRE中支持的lib等(如果你只有一个简单的应用程序,那么1 + 2很容易,只需从属性中获取类路径。)
readAllFromSystemClassPath("sun.boot.class.path");
readAllFromSystemClassPath("java.endorsed.dirs");
readAllFromSystemClassPath("java.ext.dirs");
readAllFromSystemClassPath("java.class.path");
扫描类路径并从JAR中拆分文件夹。
StringTokenizer pathTokenizer = new StringTokenizer(pPath, File.pathSeparator);
使用File.listFiles
扫描文件夹,然后使用ZipFile.entries
打开JAR。注意内部类和包访问类,你可能不希望它们。
isInner = (pClassName.indexOf('$') > -1);
将JAR中的文件名或路径转换为正确的类名(/ - >。)
final int i = fullName.lastIndexOf(File.separatorChar);
path = fullName.substring(0, i).replace(File.separatorChar, '.');
name = fullName.substring(i + 1);
现在您可以使用Reflection来加载该类并查看它。如果你只是想知道关于类的东西你可以加载它而不需要解析,或者使用像BCEL这样的字节码工程库来打开类而不将它加载到JVM中。
ClassLoader.getSystemClassLoader().loadClass(name).getModifiers() & Modifier.PUBLIC
答案 2 :(得分:0)
你可以使用一个简单的程序从jar获取所有类文件的列表,并在运行时将其转储到属性文件中,然后在程序中加载req。当需要时等级;没有使用反射。
答案 3 :(得分:0)
您可以从类加载器中获取实际的类路径。这必须包含jar文件,否则程序将无法运行。查看类路径URL以查找以“.jar”结尾的URL,并包含一些永远不会更改jar文件名称的内容(最好在最后一个“/”之后)。之后,将其作为常规jar(或zip)文件打开并阅读内容。
有几种方法可用于获取类路径。它们都不适用于每个环境和每个设置,因此您必须逐个尝试它们,直到找到适合您需要它工作的所有情况的一个。此外,有时您可能需要调整运行时上下文,例如(通常需要)将maven surefire-plugin的类加载机制替换为可选(非默认)机制之一。
从系统属性中获取类路径1:
static String[] getClasspathFromProperty() {
return System.getProperty("java.class.path").split(File.pathSeparator);
}
从classloader获取classpath 2:with maven warning:
String[] getClasspathFromClassloader() {
URLClassLoader classLoader = (URLClassLoader) (getClass().getClassLoader());
URL[] classpath = classLoader.getURLs();
if (classpath.length == 1
&& classpath[0].toExternalForm().indexOf("surefirebooter") >= 0)
{
// todo: read classpath from manifest in surefireXXXX.jar
System.err.println("NO PROPER CLASSLOADER HERE!");
System.err.println(
"Run maven with -Dsurefire.useSystemClassLoader=false "
+"-Dsurefire.useManifestOnlyJar=false to enable proper classloaders");
return null;
}
String[] classpathLocations = new String[classpath.length];
for (int i = 0; i < classpath.length; i++) {
// you must repair the path strings: "\.\" => "/" etc.
classpathLocations[i] = cleanClasspathUrl(classpath[i].toExternalform());
}
return classpathLocations;
}
从当前线程上下文获取类路径3:这类似于方法2,除了方法的第一行应如下所示:
URLClassLoader classLoader
= (URLClassLoader)(Thread.currentThread().getContextClassLoader());
祝你好运!
答案 4 :(得分:0)
我会使用像ASM这样的字节码检查器库。此ClassVisitor可用于查找主要方法:
import org.objectweb.asm.*;
import org.objectweb.asm.commons.EmptyVisitor;
public class MainFinder extends ClassAdapter {
private String name;
private boolean isMainClass;
public MainFinder() {
super(new EmptyVisitor());
}
@Override
public void visit(int version, int access, String name,
String signature, String superName,
String[] interfaces) {
this.name = name;
super.visit(version, access, name, signature,
superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name,
String desc, String signature, String[] exceptions) {
if ((access & Opcodes.ACC_PUBLIC) != 0
&& (access & Opcodes.ACC_STATIC) != 0
&& "main".equals(name)
&& "([Ljava/lang/String;)V".equals(desc)) {
isMainClass = true;
}
return super.visitMethod(access, name, desc, signature,
exceptions);
}
public String getName() {
return name;
}
public boolean isMainClass() {
return isMainClass;
}
}
请注意,您可能希望更改代码以确认类是公共的等等。
此示例应用程序在命令行指定的JAR上使用上述类:
import java.io.*;
import java.util.Enumeration;
import java.util.jar.*;
import org.objectweb.asm.ClassReader;
public class FindMainMethods {
private static void walk(JarFile jar) throws IOException {
Enumeration<? extends JarEntry> entries = jar.entries();
while (entries.hasMoreElements()) {
MainFinder visitor = new MainFinder();
JarEntry entry = entries.nextElement();
if (!entry.getName().endsWith(".class")) {
continue;
}
InputStream stream = jar.getInputStream(entry);
try {
ClassReader reader = new ClassReader(stream);
reader.accept(visitor, ClassReader.SKIP_CODE);
if (visitor.isMainClass()) {
System.out.println(visitor.getName());
}
} finally {
stream.close();
}
}
}
public static void main(String[] args) throws IOException {
JarFile jar = new JarFile(args[0]);
walk(jar);
}
}
您可能还想查看“java.class.path”系统属性。
System.getProperty("java.class.path");
可以使用反射来获得类似的结果,但是这种方法可能会产生一些不幸的副作用 - 例如导致静态初始化器运行,或者将未使用的类保留在内存中(它们可能会保持加载状态直到它们的ClassLoader是垃圾收集)。