ben*_*nyl 12
编辑01-08-2016:我添加了另一个使用SerializedLambda类的方法,该类不需要第三方软件(即ByteBuddy),您可以在标题为"使用SerializedLambda"的部分中阅读它.
原答案:问题解释+使用ByteBuddy解决它
接受的答案没有包含有关如何在运行时通过asm实际读取lambda字节代码的具体信息(即,没有javap) - 所以我想我会在此处添加此信息以供将来参考和其他人的利益.
假设以下代码:
public static void main(String[] args) {
Supplier<Integer> s = () -> 1;
byte[] bytecode = getByteCodeOf(s.getClass()); //this is the method that we need to create.
ClassReader reader = new ClassReader(bytecode);
print(reader);
}
Run Code Online (Sandbox Code Playgroud)
print(ClassReader)代码 - 如果没有看到这个问题的答案.为了通过asm读取字节码,首先需要给asm(通过ClassReader)lambda的实际字节码 - 问题是lambda类是在运行时通过LambdaMetaFactory类生成的,因此是正常的获取方法字节代码不起作用:
byte[] getByteCodeOf(Class<?> c){
//in the following - c.getResourceAsStream will return null..
try (InputStream input = c.getResourceAsStream('/' + c.getName().replace('.', '/')+ ".class")){
byte[] result = new byte[input.available()];
input.read(result);
return result;
}
}
Run Code Online (Sandbox Code Playgroud)
如果我们看一下在类的名称c通过c.getName()我们将会看到这样的defining.class.package.DefiningClass$$Lambda$x/y地方x,并y为数字,现在我们可以理解为什么上面不工作-有在类路径中没有这样的资源..
虽然JVM显然知道类的字节码,但遗憾的是,它没有现成的API允许你检索它,另一方面,JVM有一个检测API(通过代理)允许你编写一个类可以检查加载(和重新加载)类的字节码.
我们本可以编写这样的代理,并以某种方式告诉它我们想要接收lambda类的字节码 - 然后代理可以请求JVM重新加载该类(不更改它) - 这将导致代理接收字节 - 重装类的代码并将其返回给我们.
幸运的是,我们有一个名为ByteBuddy的库,已经创建了这样的代理,使用这个库 - 以下内容将起作用(如果你是一个maven用户,包括你的pom中的byte-buddy-dep和byte-buddy-agent的依赖项,另外 - 请参阅有关限制的说明).
private static final Instrumentation instrumentation = ByteBuddyAgent.install();
byte[] getByteCodeOf(Class<?> c) throws IOException {
ClassFileLocator locator = ClassFileLocator.AgentBased.of(instrumentation, c);
TypeDescription.ForLoadedType desc = new TypeDescription.ForLoadedType(c);
ClassFileLocator.Resolution resolution = locator.locate(desc.getName());
return resolution.resolve();
}
Run Code Online (Sandbox Code Playgroud)
限制: - 根据您的jvm安装,您可能必须通过命令行安装代理(请参阅ByteBuddyAgent文档和Instrumentation文档)
新答案:使用SerializedLambda
如果您尝试读取的lambda实现了一个扩展的接口Serializable- LambdaMetafactory该类实际上生成一个调用的私有方法writeReplace,该方法提供该类的实例SerializedLambda.此实例可用于检索使用LambdaMetafactory.生成的实际静态方法.
所以,例如,有两种方法可以使用"Serializable Lambda":
public class Sample {
interface SerializableRunnable extends Runnable, Serializable{}
public static void main(String... args) {
SerializableRunnable oneWay = () -> System.out.println("I am a serializable lambda");
Runnable anotherWay = (Serializable & Runnable) () -> System.out.println("I am a serializable lambda too!");
}
}
Run Code Online (Sandbox Code Playgroud)
在上述实例中都oneWay和anotherWay将有一个产生writeReplace其可以使用以下面的方式反射检索方法:
SerializedLambda getSerializedLambda(Serializable lambda) throws Exception {
final Method method = lambda.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
return (SerializedLambda) method.invoke(lambda);
}
Run Code Online (Sandbox Code Playgroud)
如果我们看一下javadoc,SerializedLambda我们会发现以下方法:
public String getImplClass():获取包含实现方法的类的名称.返回:包含实现方法的类的名称
public String getImplMethodName():获取实现方法的名称.返回:实现方法的名称
这意味着您现在可以使用ASM来读取包含lambda的类,获取实现lambda的方法并修改/读取它.
您甚至可以使用以下代码获得lambda的反射版本:
Method getReflectedMethod(Serializable lambda) throws Exception {
SerializedLambda s = getSerializedLambda(lambda);
Class containingClass = Class.forName(s.getImplClass());
String methodName = s.getImplMethodName();
for (Method m : containingClass.getDeclaredMethods()) {
if (m.getName().equals(methodName)) return m;
}
throw new NoSuchElementException("reflected method could not be found");
}
Run Code Online (Sandbox Code Playgroud)
lambda 编译为具有合成名称的静态方法。因此,要使用 ASM 读取代码,您需要对方法名称进行逆向工程……然后像任何其他方法一样读取它。
但如果您只想查看 lambda 的字节码,那么使用javap.
| 归档时间: |
|
| 查看次数: |
1380 次 |
| 最近记录: |