我试图从Java程序中检索任何类的源代码(如果有)以进行调试。假设我有Class[_]
的引用,我想检索源代码。
到目前为止,我在Scala中尝试过的事情:
val clazz = classOf[ClassDefinedInSeparateFile]
clazz.getProtectionDomain.getCodeSource.getLocation.toString + "/" +
clazz.getPackage.getName.replaceAll("\\.","/") + "/" + clazz.getSimpleName + ".scala"
-看起来不错,JAR在那里并且包含.scala
文件,但无法使用Source.fromFile(...)
打开。"/" + clazz.getPackage.getName.replaceAll("\\.","/") + "/" + clazz.getSimpleName + ".scala"
-看起来不错,但无法使用Source.fromInputStream(...)
备注:
.java
或.scala
文件,因此不需要反编译器。 (至少对于应用程序的源代码而言,但不是对依赖项的依赖。如果可以访问应用程序源代码的代码段,那就足够了-大多数异常都捕获在应用程序级别并与之相关。)谢谢。
答案 0 :(得分:3)
如果源位于jar中(位于classpath中),则需要找出确切位置。
clazz.getName.replaceAll("\\.", "/") + ".scala"
是一个不错的猜测,但但是:(1)源代码可能与类不在同一位置-可能有前缀(例如src/
或其他名称),或者(2)Scala类不必位于同名文件中-您可以在一个文件中包含多个类,该文件可以称为{{1} },某些类是在运行时生成的,等等。此外,包并不总是在scala中是目录(例如,它可能是包对象)。
如果您知道jar内的位置(并且jar在类路径中),则打开它的方法是:foo.scala
...但是,就像我上面说的那样,诀窍是弄清楚位置。这并不容易(并且没有单一的标准方法可以做到)。
您最好的选择确实是使用IDE。我了解您在生产环境中没有可用的功能,但实际上并不需要。您需要的是在具有IDE的某些计算机上可用的生产源代码,并且可以使用简单的clazz.getClassLoader.getResourceAsStream
命令来实现。
答案 1 :(得分:1)
如果我只是将源代码与类放置在同一文件夹中,则以下代码对于classpath和jars中的代码都可以正常工作
请注意,没有必要在名称前加上“ /”作为前缀,但是您应该扫描顶级类。
对于在核心Java中使用它,我深表歉意,但我不想添加任何其他依赖项,并希望尽可能清楚。
package com.stackoverflow.q53749060;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Arrays;
import java.util.stream.Stream;
import org.junit.Test;
import com.stackoverflow.q53749060.Answer.Result.Found;
import com.stackoverflow.q53749060.Answer.Result.NotFound;
import com.stackoverflow.q53749060.MyTopLevelClass.MyNestedClass;
import com.stackoverflow.q53749060.MyTopLevelClassInAnotherJar.MyNestedClassInAnotherJar;
@SuppressWarnings("javadoc")
public class Answer {
static final String[] EXTENSIONS = { "java", "scala" };
@Test
public void test() {
Arrays.stream(EXTENSIONS)
.flatMap(ext -> toSource(ext, MyTopLevelClass.class, MyNestedClass.class,MyTopLevelClassInAnotherJar.class,MyNestedClassInAnotherJar.class, String.class))
.forEach(System.out::println);
}
public Stream<Result> toSource(final String extension, final Class<?>... classes) {
return Arrays.stream(classes)
.map(clazz -> toSource(extension, clazz));
}
public Result toSource(final String extension, final Class<?> clazz) {
Class<?> topLevelClass = clazz;
while (topLevelClass.getEnclosingClass() != null) {
topLevelClass = topLevelClass.getEnclosingClass();
}
final String name = topLevelClass.getName()
.replaceAll("\\.", "/") + "." + extension;
final Thread currentThread = Thread.currentThread();
final ClassLoader contextClassLoader = currentThread.getContextClassLoader();
if (contextClassLoader.getResource(name) == null) {
return new NotFound(clazz);
}
final String source = toSource(name, contextClassLoader);
return new Found(clazz, name, source);
}
public String toSource(final String name, final ClassLoader contextClassLoader) {
try (final InputStream resourceInputStream = contextClassLoader.getResourceAsStream(name);
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
int length;
byte[] data = new byte[1024];
while ((length = resourceInputStream.read(data, 0, data.length)) != -1) {
byteArrayOutputStream.write(data, 0, length);
}
byteArrayOutputStream.flush();
byte[] byteArray = byteArrayOutputStream.toByteArray();
return new String(byteArray);
} catch (IOException ioe) {
throw new UncheckedIOException("Failed to read source file: " + name, ioe);
}
}
static class Result {
final Class<?> clazz;
Result(Class<?> clazz) {
super();
this.clazz = clazz;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((this.clazz == null) ? 0 : this.clazz.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Result other = (Result) obj;
if (this.clazz == null) {
if (other.clazz != null) {
return false;
}
} else if (!this.clazz.equals(other.clazz)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Result [clazz=" + this.clazz + "]";
}
static class Found extends Result {
final String source;
final String path;
Found(Class<?> clazz, String path, String source) {
super(clazz);
this.path = path;
this.source = source;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((this.source == null) ? 0 : this.source.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Found other = (Found) obj;
if (this.source == null) {
if (other.source != null) {
return false;
}
} else if (!this.source.equals(other.source)) {
return false;
}
return true;
}
@Override
public String toString() {
return "Found [source=" + this.source + ", clazz=" + this.clazz + "]";
}
}
static class NotFound extends Result {
NotFound(Class<?> clazz) {
super(clazz);
}
@Override
public String toString() {
return "NotFound [clazz=" + this.clazz + "]";
}
}
}
}
答案 2 :(得分:0)
如果要获得类似于Java原始源代码的内容,则需要Java反编译器。
您将无法访问源代码(您的可执行程序由Java bytecode组成,而不是Java源代码)。
这是指向我的favorite Java Decompiler的链接。