ПРИМЕЧАНИЯ
- Я предположил, что ваша ситуация достаточно сложна, чтобы простой или расширенный текстовый поиск в базе кода не помог вам.
- Это взломанное, а не универсальное решение, предназначенное только для тестирования и диагностики.
- Чтобы использовать этот хак, вы должны иметь возможность изменить свой код и заменить фактическую модель экземпляром прокси во время тестирования / диагностики. Если вы не можете этого сделать, то вам придется использовать еще более продвинутый способ взлома, то есть разработку байт-кода с помощью BCEL, ASM и т. Д.
- Динамические прокси имеют недостатки в производительности кода, поэтому не являются идеальным выбором для производственного режима.
- Использование карты для хранения модели не очень хорошая идея. Вместо этого следует использовать четко определенную систему типов, то есть классы Java.
Общий шаблон проектирования для такой проблемы: proxy . Промежуточный объект между вашей фактической моделью и вызывающим абонентом, который может перехватывать вызовы, собирать статистику или даже мешать исходному вызову. Прокси-модель в конечном итоге отправляет все в фактическую модель
Очевидный прокси - это просто обернуть фактическую модель в другую карту, например
public class MapProxy<K, V> implements Map<K, V> {
public MapProxy(final Map<K, V> actual) {
}
// implement ALL methods and redirect them to the actual model
}
Теперь рефлексия не поможет вам в этом напрямую, но может помочь быстрее реализовать прокси с помощью динамических прокси ( Динамические прокси-классы ), например,
@SuppressWarnings("unchecked")
private Map<String, Object> proxy(final Map<String, Object> model) {
final InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Collect usage stats or intervene
return method.invoke(model, args);
}
};
return (Map<String, Object>) Proxy.newProxyInstance(Map.class.getClassLoader(),
new Class<?>[] { Map.class }, handler);
}
ПРИМЕЧАНИЕ : В любом случае вам необходимо заменить фактическую модель на модель с прокси как минимум на время вашего теста.
С помощью другого трюка вы можете узнать , кто вызвал метод вашей модели . Просто зайдите на Thread.currentThread().getStackTrace()
и получите соответствующий элемент.
Теперь собираем все кусочки вместе:
InvocationLog.java
public final class InvocationLog {
private Method method;
private Object[] arguments;
private StackTraceElement caller;
public InvocationLog(Method method, Object[] arguments, StackTraceElement caller) {
this.method = method;
this.arguments = arguments;
this.caller = caller;
}
public Method getMethod() { return this.method; }
public Object[] getArguments() { return this.arguments; }
public StackTraceElement getCaller() { return this.caller; }
@Override
public String toString() {
return String.format("%s (%s): %s",
method == null ? "<init>" : method.getName(),
arguments == null ? "" : Arrays.toString(arguments),
caller == null ? "" : caller.toString());
}
}
ModelWatch.java
public final class ModelWatch {
private final Map<String, Object> modelProxy;
private final List<InvocationLog> logs = new ArrayList<>();
public ModelWatch(final Map<String, Object> model) {
modelProxy = proxy(model);
}
@SuppressWarnings("unchecked")
private Map<String, Object> proxy(final Map<String, Object> model) {
final InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method, args, Thread.currentThread().getStackTrace());
return method.invoke(model, args);
}
};
return (Map<String, Object>) Proxy.newProxyInstance(Map.class.getClassLoader(),
new Class<?>[] { Map.class }, handler);
}
private void log(Method method, Object[] arguments, StackTraceElement[] stack) {
logs.add(new InvocationLog(method, arguments, stack[3]));
// 0: Thread.getStackTrace
// 1: InvocationHandler.invoke
// 2: <Proxy>
// 3: <Caller>
}
public Map<String, Object> getModelProxy() { return modelProxy; }
public List<InvocationLog> getLogs() { return logs; }
}
Чтобы использовать его:
private Map<String, Object> actualModel = new HashMap<String, Object>();
private ModelWatch modelWatch = new ModelWatch(model);
private Map<String, Object> model = modelWatch.getModelProxy();
// Calls to model ...
modelWatch.getLogs() // Retrieve model activity