我知道以前有人问过这个问题,答案通常是“你不能”和/或“不要”,但无论如何我都在尝试。
上下文是我试图设置一些“黑魔法”来帮助测试。我的代码最终在 JUnit 下运行,并且系统的性质是这样的,虽然我可以访问我想要的大多数库(ByteBuddy、Javassist 等),但我无法在代码运行之前玩弄它,我一直在忙着上课。
这是设置:
// External Library that I have no control over:
package com.external.stuff;
/** This is the thing I ultimately want to capture a specific instance of. */
public class Target {...}
public interface IFace {
void someMethod();
}
class IFaceImpl {
@Override
void someMethod() {
...
Target t = getTarget(...);
doSomethingWithTarget(t);
...
}
private Target getTarget() {...}
private void doSomethingWithTarget(Target t) {...}
}
在我的测试魔法中,我有一个 IFace 实例,我碰巧知道它是一个 IFaceImpl。什么我喜欢做的是能够窃取的实例Target
内部产生。实际上,这将具有与以下相同的效果(如果私有方法是可覆盖的):
class MyIFaceImpl extends IFaceImpl{
private Consumer<Target> targetStealer;
@Override
void someMethod() {
...
Target t = getTarget(...);
doSomethingWithTarget(t);
...
}
/** "Override" either this method or the next one. */
private Target getTarget() {
Target t = super.getTarget();
targetStealer.accept(t);
return t;
}
private void doSomethingWithTarget(Target t) {
targetStealer.accept(t);
super.doSomethingWithTarget(t);
}
}
但是,当然,这不起作用,因为私有方法不能被覆盖。因此,下一种方法将类似于ByteBuddy
或Javassist
public static class Interceptor {
private final Consumer<Target> targetStealer;
// ctor elided
public void doSomethingWithTarget(Target t) {
targetStealer.accept(t);
}
}
/** Using ByteBuddy. */
IFace byteBuddyBlackMagic(
IFace iface /* known IFaceImpl*/,
Consumer<Target> targetStealer) {
return (IFace) new ByteBuddy()
.subClass(iface.getClass())
.method(ElementMatchers.named("doSomethingWithTarget"))
.intercept(MethodDelegation.to(new Interceptor(t))
.make()
.load(...)
.getLoaded()
.newInstance()
}
/** Or, using Javassist */
IFace javassistBlackMagic(
IFace iface /* known IFaceImpl*/,
Consumer<Target> targetStealer) {
ProxyFactory factory = new ProxyFactory();
factory.setSuperClass(iface.getClass());
Class subClass = factory.createClass();
IFace = (IFace) subClass.newInstance();
MethodHandler handler =
new MethodHandler() {
@Override
public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
if (thisMethod.getName().equals("doSomethingWithTarget")) {
consumer.accept((Target) args[0]);
}
return proceed.invoke(self, args);
}
};
((ProxyObject) instance).setHandler(handler);
return instance;
}
当我测试这些模式时,它在我想要拦截的方法是包本地的其他情况下工作,但不适用于私有方法(预计 ByteBuddy,根据文档)。
所以,是的,我认识到这是试图调用黑暗力量,这通常是不受欢迎的。问题仍然存在,这可行吗?
使用 javassist,您可以检测 IClassImpl 类中的 someMethod() 以将 TargetClass 的实例发送到其他类并将其存储在那里或使用创建的实例进行其他操作。
这可以使用 javassist 中的 insertAfter() 方法来实现。
例如 :
method.insertAfter( "TestClass.storeTargetInst(t)" ); // t is the instance of Target class in IClassImpl.someMethod
TestClass{ public static void storeTargetInst(Object o){ ### code to store instance ###} }
insertAfter() 方法在方法的 return 语句之前或在 void 方法的情况下作为方法的最后一行注入一行代码。
有关可用于仪器的方法的更多信息,请参阅此链接。希望这可以帮助!
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句