gpt4 book ai didi

java - 使用不同的类加载器在 Java 中动态创建 lambda

转载 作者:搜寻专家 更新时间:2023-10-30 21:33:20 24 4
gpt4 key购买 nike

我正在尝试使用在编译时不可用的类动态创建 lambda 实例,因为它们是在运行时生成的,或者在编译时未知。

这可以使用以下代码

// lambdaClass = class of the lambda interface
// className = class containing the target method
// methodName = name of the target method
private static <T> T lookupLambda(Class<T> lambdaClass, String className, String methodName, Class<?> returnType,
Class<?> argumentType) throws Throwable {
MethodType lambdaType = MethodType.methodType(lambdaClass);
MethodType methodType = MethodType.methodType(returnType, argumentType);
MethodHandles.Lookup lookup = MethodHandles.lookup();
Class<?> targetClass = Class.forName(className);
MethodHandle handle = lookup.findStatic(targetClass, methodName, methodType);
CallSite callSite = LambdaMetafactory
.metafactory(lookup, "call", lambdaType, methodType.unwrap(), handle, methodType);
MethodHandle methodHandle = callSite.getTarget();

return lambdaClass.cast(methodHandle.invoke());
}

一个潜在的调用可能是这样的

@FunctionalInterface
interface MyLambda {
double call(double d);
}

public void foo() {
lookupLambda(MyLambda.class, "java.lang.Math", "sin", double.class, double.class);
}

在实验设置中,这很有效。然而,在实际代码中,lambda class 使用与应用程序其余部分不同的 ClassLoader 加载,即目标方法的 class。这会导致运行时出现异常,因为它似乎使用目标方法 classClassLoader 来加载 lambda class。这是堆栈跟踪中有趣的部分:

Caused by: java.lang.NoClassDefFoundError: GeneratedPackage.GeneratedClass$GeneratedInterface
at sun.misc.Unsafe.defineAnonymousClass(Native Method)
at java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:326)
at java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:194)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:304)
at my.project.MyClass.lookupLambda(MyClass.java:765)
at
... 9 more
Caused by: java.lang.ClassNotFoundException: GeneratedPackage.GeneratedClass$GeneratedInterface
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 15 more

我该如何解决这个问题?有没有办法指定每个类使用哪个 ClassLoader?是否有另一种动态创建不会遇到此问题的 lambda 实例的方法?非常感谢任何帮助。

编辑:这是一个应该显示问题的小可执行示例

<强>1。主类

import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class Test {

private static <T> T lookupLambda(Class<T> lambdaClass, String className, String methodName, Class<?> returnType,
Class<?> argumentType) throws Throwable {
MethodType lambdaType = MethodType.methodType(lambdaClass);
MethodType methodType = MethodType.methodType(returnType, argumentType);
MethodHandles.Lookup lookup = MethodHandles.lookup();
Class<?> targetClass = Class.forName(className);
MethodHandle handle = lookup.findStatic(targetClass, methodName, methodType);
CallSite callSite = LambdaMetafactory
.metafactory(lookup, "call", lambdaType, methodType.unwrap(), handle, methodType);
MethodHandle methodHandle = callSite.getTarget();

return lambdaClass.cast(methodHandle.invoke());
}

public static void main(String[] args) throws Throwable {
URL resourcesUrl = new URL("file:/home/pathToGeneratedClassFile/");
ClassLoader classLoader = new URLClassLoader(new URL[] { resourcesUrl }, Thread.currentThread().getContextClassLoader());

Class<?> generatedClass = classLoader.loadClass("GeneratedClass");
Class<?> generatedLambdaClass = classLoader.loadClass("GeneratedClass$GeneratedLambda");

Constructor constructor = generatedClass.getConstructor(generatedLambdaClass);
Object instance = constructor
.newInstance(lookupLambda(generatedLambdaClass, "java.lang.Math", "sin", double.class, double.class));

Method method = generatedClass.getDeclaredMethod("test");
method.invoke(instance);
}

}

<强>2。生成的类这假设该类已经被编译为 .class 文件,并且它位于系统类加载器范围之外的某个地方。

import javax.annotation.Generated;

@Generated("This class is generated and loaded using a different classloader")
public final class GeneratedClass {
@FunctionalInterface
public interface GeneratedLambda {
double call(double d);
}

private final GeneratedLambda lambda;

public GeneratedClass(GeneratedLambda lambda) {
this.lambda = lambda;
}

public void test() {
System.out.println(lambda.call(3));
}

}

对我来说,这会产生以下堆栈跟踪

Exception in thread "main" java.lang.NoClassDefFoundError: GeneratedClass$GeneratedLambda
at sun.misc.Unsafe.defineAnonymousClass(Native Method)
at java.lang.invoke.InnerClassLambdaMetafactory.spinInnerClass(InnerClassLambdaMetafactory.java:326)
at java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:194)
at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:304)
at Test.lookupLambda(Test.java:21)
at Test.main(Test.java:36)
Caused by: java.lang.ClassNotFoundException: GeneratedClass$GeneratedLambda
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 6 more

最佳答案

我不知道你是如何创建你的类加载器的,但假设你已经有一个那么你可以替换

Class<?> targetClass = Class.forName(className);

Class<?> targetClass = yourClassLoader.loadClass(className);

关于java - 使用不同的类加载器在 Java 中动态创建 lambda,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48429948/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com