49 votes

Comment trouver les méthodes annotées dans un paquet donné ?

J'ai une annotation de marqueur simple pour les méthodes (similaire au premier exemple de l'article 35 dans Java efficace (2e éd.)) :

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

Ensuite, dans un paquet donné (disons com.acme.installer ), qui a quelques sous-paquets contenant une vingtaine de classes, je voudrais trouver toutes les méthodes qui sont annotées avec elle. (Parce que je voudrais faire quelques vérifications concernant toutes les méthodes annotées dans un test unitaire).

Quel est le moyen le plus simple de le faire (le cas échéant) ? De préférence sans ajouter de nouvelles bibliothèques ou de nouveaux frameworks tiers.

Modifier : pour clarifier, évidemment method.isAnnotationPresent(InstallerMethod.class) sera le moyen de vérifier si une méthode possède l'annotation - mais ce problème inclut la recherche de toutes les méthodes.

51voto

parkr Points 1140

Si vous voulez l'implémenter vous-même, ces méthodes trouveront toutes les classes d'un paquetage donné :

/**
 * 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();
        dirs.add(new File(resource.getFile()));
    }
    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;
}

Ensuite, vous pouvez simplement filtrer sur ces classes avec l'annotation donnée :

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

35voto

Vous devriez probablement jeter un coup d'oeil à l'open source Bibliothèque de réflexion . Avec lui, vous pouvez facilement réaliser ce que vous voulez avec quelques lignes de code :

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

1voto

skaffman Points 197885

Si vous êtes heureux d'utiliser Spring, alors il fait quelque chose dans ce sens en utilisant sa fonctionnalité context:component-scan, où Spring recherche les classes annotées dans un paquetage donné. Sous la couverture, c'est assez horrible, et cela implique de fouiller dans le système de fichiers et dans les fichiers JAR à la recherche de classes dans le paquet.

Même si vous ne pouvez pas utiliser Spring directement, un coup d'œil à son code source peut vous donner quelques idées.

Il est certain que l'APi de réflexion de Java n'est pas utile ici, il ne fournit pas spécifiquement un moyen d'obtenir toutes les classes dans un paquet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X