Я знаю, что об этом уже спрашивали, и ответ обычно - "ты не можешь" и / или "нет", но я все равно пытаюсь это сделать.
Ситуация такова, что яЯ пытаюсь настроить «черную магию» для помощи в тестировании.Мой код работает в конечном счете под 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, согласно документации ).
Итак, да, я признаю, что это пытается вызвать темные силы, и что это обычно хмуритсяна.Остается вопрос, это выполнимо?