Подтвердить что ты не робот

Как найти аннотированные методы в данном пакете?

У меня есть простая маркерная аннотация для методов (аналогично первому примеру в пункте 35 в Эффективной Java (2-е изд.)):

/**
 * Marker annotation for methods that are called from installer 
 * validation scripts etc. 
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface InstallerMethod {
}

Затем в данном пакете (скажем com.acme.installer), который содержит несколько подпакетов, содержащих около 20 классов, я хотел бы найти все методы, которые аннотируются с ним. (Потому что я хотел бы сделать некоторые проверки относительно всех аннотированных методов в unit test.)

Что (если есть) - это самый простой способ сделать это? Предпочтительно без добавления новых сторонних библиотек или фреймворков.

Изменить: для уточнения, очевидно, method.isAnnotationPresent(InstallerMethod.class) будет способ проверить, имеет ли метод аннотацию, но эта проблема включает в себя поиск всех методов.

4b9b3361

Ответ 1

Если вы хотите реализовать его самостоятельно, эти методы найдут все классы в данном пакете:

/**
 * Scans all classes accessible from the context class loader which belong
 * to the given package and subpackages.
 * 
 * @param packageName
 *            The base package
 * @return The classes
 * @throws ClassNotFoundException
 * @throws IOException
 */
private Iterable<Class> getClasses(String packageName) throws ClassNotFoundException, IOException
{
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    String path = packageName.replace('.', '/');
    Enumeration<URL> resources = classLoader.getResources(path);
    List<File> dirs = new ArrayList<File>();
    while (resources.hasMoreElements())
    {
        URL resource = resources.nextElement();
        URI uri = new URI(resource.toString());
        dirs.add(new File(uri.getPath()));
    }
    List<Class> classes = new ArrayList<Class>();
    for (File directory : dirs)
    {
        classes.addAll(findClasses(directory, packageName));
    }

    return classes;
}

/**
 * Recursive method used to find all classes in a given directory and
 * subdirs.
 * 
 * @param directory
 *            The base directory
 * @param packageName
 *            The package name for classes found inside the base directory
 * @return The classes
 * @throws ClassNotFoundException
 */
private List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException
{
    List<Class> classes = new ArrayList<Class>();
    if (!directory.exists())
    {
        return classes;
    }
    File[] files = directory.listFiles();
    for (File file : files)
    {
        if (file.isDirectory())
        {
            classes.addAll(findClasses(file, packageName + "." + file.getName()));
        }
        else if (file.getName().endsWith(".class"))
        {
            classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
        }
    }
    return classes;
}

Затем вы можете просто фильтровать эти классы с помощью данной аннотации:

for (Method method : testClass.getMethods())
{
    if (method.isAnnotationPresent(InstallerMethod.class))
    {
        // do something
    }
}

Ответ 2

Вероятно, вам стоит взглянуть на открытую библиотеку Reflections. С его помощью вы легко можете достичь того, чего хотите, с несколькими строками кода:

Reflections reflections = new Reflections( 
    new ConfigurationBuilder().setUrls( 
    ClasspathHelper.forPackage( "com.acme.installer" ) ).setScanners(
    new MethodAnnotationsScanner() ) );
Set<Method> methods = reflections.getMethodsAnnotatedWith(InstallerMethod.class);

Ответ 3

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

Даже если вы не можете напрямую использовать Spring, просмотр исходного кода может дать вам некоторые идеи.

Разумеется, явное отражение APi здесь бесполезно, оно специально не предоставляет средства для получения всех классов в пакете.