Java 以编程方式确定不需要导入的JRE类列表
我需要以编程方式找出哪些JRE类可以在编译单元中引用,而无需导入(用于静态代码分析)。我们可以忽略包本地类,包Java 以编程方式确定不需要导入的JRE类列表,java,classloader,urlclassloader,Java,Classloader,Urlclassloader,我需要以编程方式找出哪些JRE类可以在编译单元中引用,而无需导入(用于静态代码分析)。我们可以忽略包本地类,包java.lang中的类被隐式导入。输出应该是一个列表。该解决方案应与纯Java 5及以上版本(无番石榴等)配合使用,且不依赖供应商 欢迎任何可靠的基于Java的解决方案 以下是我到目前为止尝试的一些注意事项: 乍一看,问题似乎归结为“如何从一个包中加载所有类?”,这当然几乎是不可能的,尽管存在一些解决方法(例如and,以及链接在那里的博客文章)。但我的情况要简单得多,因为不存在多类加
java.lang
中的类被隐式导入。输出应该是一个列表。该解决方案应与纯Java 5及以上版本(无番石榴等)配合使用,且不依赖供应商
欢迎任何可靠的基于Java的解决方案
以下是我到目前为止尝试的一些注意事项: 乍一看,问题似乎归结为“如何从一个包中加载所有类?”,这当然几乎是不可能的,尽管存在一些解决方法(例如and,以及链接在那里的博客文章)。但我的情况要简单得多,因为不存在多类加载器问题
java.lang
stuff总是可以由系统/引导类加载器加载,并且您不能在该包中创建自己的类。问题是,系统类加载器没有泄露其类路径,而链接的方法依赖于它
到目前为止,我还无法访问系统类加载器的类路径,因为在我使用的HotSpot VM上,Object.class.getClassLoader()
返回null
,并且Thread.currentThread().getContextClassLoader()
可以委托加载java.lang.Object
,但它本身不包括类路径。所以像这样的解决方案对我不起作用。另外,不包括具有此类类路径信息的属性(例如sun.boot.class.path
)
如果我不必假设存在rt.jar,而是扫描系统类加载器使用的资源列表,那就太好了。对于特定于供应商的JRE实现,这种方法会更安全。好的,我误解了这个问题。检查JLS时,我看到的是: 每个编译单元隐式地导入预定义包java.lang中声明的每个公共类型名称,就像声明import java.lang.*;出现在每个编译单元的开头,紧跟在任何包语句之后。因此,所有这些类型的名称在每个编译单元中都可以作为简单名称使用 ()
如果您想知道包含哪些类型,它将随Java版本的不同而变化…编译类似乎包含可读的
Java/lang
文本。所以我写了一些代码,看看这些导入是否可以被提取。这是一个黑客攻击,所以不可靠,但假设您可以提取/列出jar文件中的所有类,这可能是一个起点
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
public class Q21102294 {
public static final String EXTERNAL_JAR = "resources/appboot-1.1.1.jar";
public static final String SAMPLE_CLASS_NAME = "com/descartes/appboot/AppBoot.class";
public static HashSet<String> usedLangClasses = new HashSet<String>();
public static void main(String[] args) {
try {
Path f = Paths.get(EXTERNAL_JAR);
if (!Files.exists(f)) {
throw new RuntimeException("Could not find file " + f);
}
URLClassLoader loader = new URLClassLoader(new URL[] { f.toUri().toURL() }, null);
findLangClasses(loader, SAMPLE_CLASS_NAME);
ArrayList<String> sortedClasses = new ArrayList<String>();
sortedClasses.addAll(usedLangClasses);
Collections.sort(sortedClasses);
System.out.println("Loaded classes: ");
for (String s : sortedClasses) {
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void findLangClasses(URLClassLoader loader, String classResource) throws Exception {
URL curl = loader.getResource(classResource);
if (curl != null) {
System.out.println("Got class as resource.");
} else {
throw new RuntimeException("Can't open resource.");
}
ByteArrayOutputStream bout = new ByteArrayOutputStream();
InputStream in = curl.openStream();
try {
byte[] buf = new byte[8192];
int l = 0;
while ((l = in.read(buf)) > -1) {
bout.write(buf, 0, l);
}
} finally {
in.close();
}
String ctext = new String(bout.toByteArray(), StandardCharsets.UTF_8);
int offSet = -1;
while ((offSet = ctext.indexOf("java/lang/", offSet)) > -1) {
int beginIndex = offSet;
offSet += "java/lang/".length();
char cnext = ctext.charAt(offSet);
while (cnext != ';' && (cnext == '/' || Character.isAlphabetic(cnext))) {
offSet += 1;
cnext = ctext.charAt(offSet);
}
String langClass = ctext.substring(beginIndex, offSet);
//System.out.println("adding class " + langClass);
usedLangClasses.add(langClass);
}
}
}
所用编译类的源代码可用。请注意,您提到的JLS段落中说这些包是“可观察的”,但这并不意味着它们是隐式导入的。第7.3段说总是有一个隐式的
import java.lang.*代码>@Jesper谢谢,你说得对。这也是我在经验上观察到的。我相应地更新了问题。你忘了本地课程包了吗?@JoopEggen谢谢。AFAIK,java.lang
是一个被禁止的包名。我澄清了关于在java.lang
之外打包本地类的问题。是的!这比我的描述更能说明问题:-)跳出框框思考得好!但是,它并没有生成java.lang
类的完整列表(仅限于引用的类)。此外,它还需要访问已编译的类文件(目前我还没有,但很好)。不过,还是要感谢您提出的有趣的新方法!啊哈,我现在更明白这个问题了。希望有人有另一个创意火花。
Got class as resource.
Loaded classes:
java/lang/Class
java/lang/ClassLoader
java/lang/Exception
java/lang/Object
java/lang/RuntimeException
java/lang/String
java/lang/StringBuilder
java/lang/System
java/lang/Thread
java/lang/Throwable
java/lang/reflect/Method