Найти все переходные зависимости Java Class, включая классы реализации, используемые только через их интерфейсы

Моя цель состоит в том, чтобы перечислить все переходные зависимости классов открытого API моего проекта и использовать это, чтобы сосредоточить усилия на тестировании в случае любых изменений кода, внесенных в эти зависимости.

Например:

class MyApi {
    MyDao md;
    public void methodA() {
        //do something with md;
    }
}

interface MyDao { }

class MyDaoImpl implements MyDao { }

Поэтому, если я знаю, что MyDaoImpl был изменен (скажем, из истории коммитов), и я знаю, что MyApi.methodA использует MyDaoImpl, то мое тестирование должно быть сосредоточено на его проверке. Мне нужен список зависимостей MyApi.methodA(), включая MyDao и MyDaoImpl.

До сих пор я попробовал два инструмента - https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jdeps.html и http://depfind.sourceforge.net/ - они многообещающие, но кажется, не полностью решить проблему. Для обоих инструментов кажется, что если класс зависит от интерфейса, не существует встроенного способа включить реализации этого интерфейса в качестве транзитивных зависимостей.

Есть ли способ извлечь эту информацию из каких-либо инструментов без особых настроек?

1 ответ

Решение

Вы можете использовать JArchitect для своих нужд. Если щелкнуть правой кнопкой мыши метод в любом месте пользовательского интерфейса и выбрать меню: выберите метод... > ... который использует меня (прямо или косвенно), вы получите запрос кода:

from m in Methods 
let depth0 = m.DepthOfIsUsing("myNamespace.MyClass.MyMethod()")
where depth0  >= 0 orderby depth0
select new { m, depth0 }

Проблема в том, что такой запрос дает косвенное использование, но не ищет вызовы, которые происходят через интерфейс (или переопределенный метод, объявленный в базовом классе).

Надеюсь, то, что вы просите, можно получить с помощью этого запроса:

// Retrieve the target method by name
let methodTarget = Methods.WithFullName(""myNamespace.MyClass.MyMethod()"").Single()

// Build a ICodeMetric<IMethod,ushort> representing the depth of indirect
// call of the target method.
let indirectCallDepth = 
   methodTarget.ToEnumerable()
   .FillIterative(
       methods => methods.SelectMany(
          m => m.MethodsCallingMe.Union(m.OverriddensBase)))

from m in indirectCallDepth.DefinitionDomain
select new { m, callDepth = indirectCallDepth[m]  }

Два краеугольных камня этого запроса:

  • Вызов FillIterative() для рекурсивного выбора косвенного вызова.
  • Вызов свойства IMethod.OverriddensBase, как следует из его названия. Для метода M это возвращает перечислимый из всех методов, объявленных в базовом классе или интерфейсе, переопределенных M.
Другие вопросы по тегам