kaq*_*qao 2 java code-generation bytecode java-bytecode-asm byte-buddy
我有一个类似的界面:
public interface Getter {
Object get(Params params);
}
Run Code Online (Sandbox Code Playgroud)
我使用对不同方法的反射调用来实现:
public class GetterImpl implements Getter {
private final Object target;
private final Method method; //doStuff method
public GetterImpl(Object target, Method method) {
this.target = target;
this.method = method;
}
@Override
public Object get(Params params) {
//both the target and arguments depend on Params
return method.invoke(chooseTarget(params), prepareArgs(params));
}
private Object chooseTarget(Params params) {
if (params.getTargetOverride() != null) {
return params.getTargetOverride();
}
return target;
}
private Object[] prepareArgs(Params params) {
...
}
}
Run Code Online (Sandbox Code Playgroud)
是否可以生成一个Getter使用等效逻辑实现但没有反射的类?有效的类是这样的:
public class GeneratedGetterImpl implements Getter {
...
@Override
public Object get(Params params) {
//somehow call doStuff directly (different method for each generated impl)
return target.doStuff(prepareArgs(params));
}
}
Run Code Online (Sandbox Code Playgroud)
我正在考虑使用 Byte Buddy 动态生成这样的类,但所有示例都提供了某种静态已知的方法拦截器,并且从不委托给动态选择的目标和方法。
这显然不是一项微不足道的任务,但这可以通过 Byte Buddy 完成吗?还是不同的图书馆?
更新:
这是我迄今为止最好的尝试:
Target target = new Target();
Method method = Target.class.getMethod("doStuff", Book.class);
//Helper class that computes the new arguments based on the original
Prepare prepare = new Prepare();
Method doPrep = Prepare.class.getMethod("doPrep", Params.class);
Getter getter = (Getter) new ByteBuddy()
.subclass(Object.class)
.implement(Getter.class)
.method(named("get")).intercept(
MethodCall.invoke(method).on(target)
.withMethodCall(
MethodCall.invoke(doPrep).on(prepare).withAllArguments()
))
.make()
.load(getClass().getClassLoader())
.getLoaded()
.newInstance();
public static class Prepare {
public Book doPrep(Params params) {
return new Book(params.getTitle());
}
}
Run Code Online (Sandbox Code Playgroud)
这符合我的要求,但前提是目标方法采用 1 个参数(Book在我的情况下)。我正在努力弄清楚如何让它返回一个数组,然后在调用目标方法时传播该数组。
例如
public static class Prepare {
//returns all the arguments
public Object[] doPrep(Params params) {
return new Object[] { new Book(params.getTitle()) };
}
}
Run Code Online (Sandbox Code Playgroud)
如果我们将其限制为将接口绑定到匹配的目标方法,则 JRE 中确实已经存在这样的工具。
public static void main(String[] args) throws NoSuchMethodException {
Function<Double,Double> f1 = create(Math.class.getMethod("abs", double.class));
System.out.println(f1.apply(-42.0));
Map<Double,Double> m = new HashMap<>();
Function<Double,Double> f2 = create(Map.class.getMethod("get", Object.class), m);
m.put(1.0, 123.0);
System.out.println(f2.apply(1.0));
}
static Function<Double,Double> create(Method m) {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(Double.class, Double.class);
try {
return (Function)LambdaMetafactory.metafactory(l, "apply",
MethodType.methodType(Function.class), t.erase(), l.unreflect(m), t)
.getTarget().invoke();
} catch(Throwable ex) {
throw new IllegalStateException(ex);
}
}
static Function<Double,Double> create(Method m, Object target) {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(Double.class, Double.class);
try {
return (Function)LambdaMetafactory.metafactory(l, "apply",
MethodType.methodType(Function.class, m.getDeclaringClass()),
t.erase(), l.unreflect(m), t)
.getTarget().invoke(target);
} catch(Throwable ex) {
throw new IllegalStateException(ex);
}
}
Run Code Online (Sandbox Code Playgroud)
public static void main(String[] args) throws NoSuchMethodException {
Function<Double,Double> f1 = create(Math.class.getMethod("abs", double.class));
System.out.println(f1.apply(-42.0));
Map<Double,Double> m = new HashMap<>();
Function<Double,Double> f2 = create(Map.class.getMethod("get", Object.class), m);
m.put(1.0, 123.0);
System.out.println(f2.apply(1.0));
}
static Function<Double,Double> create(Method m) {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(Double.class, Double.class);
try {
return (Function)LambdaMetafactory.metafactory(l, "apply",
MethodType.methodType(Function.class), t.erase(), l.unreflect(m), t)
.getTarget().invoke();
} catch(Throwable ex) {
throw new IllegalStateException(ex);
}
}
static Function<Double,Double> create(Method m, Object target) {
MethodHandles.Lookup l = MethodHandles.lookup();
MethodType t = MethodType.methodType(Double.class, Double.class);
try {
return (Function)LambdaMetafactory.metafactory(l, "apply",
MethodType.methodType(Function.class, m.getDeclaringClass()),
t.erase(), l.unreflect(m), t)
.getTarget().invoke(target);
} catch(Throwable ex) {
throw new IllegalStateException(ex);
}
}
Run Code Online (Sandbox Code Playgroud)
这表明包括通用函数所需的自动装箱和强制转换等适应,但参数或结果的任何其他适应都是不可能的,必须由预先存在的装饰代码执行。最值得注意的是,不包括可变参数处理。
文档是详尽的。强烈建议在使用该课程之前阅读所有详细信息。但是在这里你可能做错的事情,类似于你在实现自己的字节码生成器时可能做错的事情。