Java - как анализировать код функции - PullRequest
0 голосов
/ 02 июля 2018

Мы работаем с шаблоном mvc design, где все данные хранятся под картой.

Я хочу перебрать все классы в системе и для каждого проверить, что метод помещает на карту и что метод получает с карты.

Например, для следующего кода:

private void myFunc()
{
Object obj = model.get("mykey");
Object obj2 = model.get("mykey2");
.....
model.put("mykey3", "aaa");
}

Я хочу знать, что в этой функции у нас есть 2 get: mykey и mykey2 и 1 put: mykey3

Как я могу сделать это с помощью кода.

Спасибо.

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

ПРИМЕЧАНИЯ

  • Я предположил, что ваша ситуация достаточно сложна, чтобы простой или расширенный текстовый поиск в базе кода не помог вам.
  • Это взломанное, а не универсальное решение, предназначенное только для тестирования и диагностики.
  • Чтобы использовать этот хак, вы должны иметь возможность изменить свой код и заменить фактическую модель экземпляром прокси во время тестирования / диагностики. Если вы не можете этого сделать, то вам придется использовать еще более продвинутый способ взлома, то есть разработку байт-кода с помощью 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
0 голосов
/ 02 июля 2018

Вы пометили это как "отражение", но это не сработает. Отражение позволяет только проверять «подписи». Вы можете использовать его для идентификации методов класса и аргументов методов.

Это абсолютно не поможет вам определить , что делает каждый метод.

Для того, чтобы узнать об этом, вам нужно либо проанализировать сторону исходного кода java, либо классы байт-кода. Как в: написать код, который читает этот контент и понимает «достаточно» его, чтобы найти такие места. Что является очень сложным усилием. И, конечно же, очень легко обойти весь такой код "сканера", выполнив такие действия, как:

List<String> keysToUpdate = Arrays.asList("key1", "key2");
for (String key : keysToUpdate) { 
  ... does something about each key

Взрыва. Как бы вы написали код, который надежно находит ключи для этого? Когда вы нашли этот код, теперь представьте, что список создан не там, а далеко и в качестве аргумента? Когда вы выяснили, как это решить, теперь рассмотрим код, который использует отражение для получения объекта модели и вызывает метод для этого. Увидеть? Для любого «сканера», который вы записываете, найдутся способы сделать это неудачным.

Таким образом, настоящий ответ заключается в том, что вы уже спускаетесь не по той кроличьей норе:

Вы должны никогда написать:

Object obj = model.get("mykey");

но что-то вроде

 Object obj = model.get(SOME_CONSTANT_FOR_KEY_X);

Значение: нет хорошего способа контролировать такие вещи. лучшее , которое вы можете сделать, это убедиться, что все ключи являются константами, исходящими из центрального места. Потому что тогда вы можете, по крайней мере, войти, и для каждого ключа в этом списке констант вы можете попросить свою IDE рассказать об их использовании.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...