Come trovare i metodi annotati in un determinato pacchetto?

Ho una semplice annotazione marcatore per metodi (simile al primo esempio in Articolo 35 in Java efficace (2a ed)):

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

Quindi, in un determinato pacchetto (ad esempio com.acme.installer ), che ha alcuni pacchetti com.acme.installer contenenti circa 20 classi, mi piacerebbe trovare tutti i metodi che sono annotati con esso. (Perché mi piacerebbe fare alcuni controlli riguardanti tutti i metodi annotati in un test unitario.)

Cosa (se c’è) è il modo più semplice per farlo? Preferibilmente senza aggiungere nuove librerie o framework di terze parti.

Modifica : per chiarire, ovviamente method.isAnnotationPresent(InstallerMethod.class) sarà il modo per verificare se un metodo ha l’annotazione – ma questo problema include la ricerca di tutti i metodi.

Se vuoi implementarlo tu stesso, questi metodi troveranno tutte le classi in un determinato pacchetto:

 /** * Scans all classs accessible from the context class loader which belong * to the given package and subpackages. * * @param packageName * The base package * @return The classs * @throws ClassNotFoundException * @throws IOException */ private Iterable getClasses(String packageName) throws ClassNotFoundException, IOException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); String path = packageName.replace('.', '/'); Enumeration resources = classLoader.getResources(path); List dirs = new ArrayList(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); URI uri = new URI(resource.toString()); dirs.add(new File(uri.getPath())); } List classs = new ArrayList(); for (File directory : dirs) { classs.addAll(findClasses(directory, packageName)); } return classs; } /** * Recursive method used to find all classs in a given directory and * subdirs. * * @param directory * The base directory * @param packageName * The package name for classs found inside the base directory * @return The classs * @throws ClassNotFoundException */ private List findClasses(File directory, String packageName) throws ClassNotFoundException { List classs = new ArrayList(); if (!directory.exists()) { return classs; } File[] files = directory.listFiles(); for (File file : files) { if (file.isDirectory()) { classs.addAll(findClasses(file, packageName + "." + file.getName())); } else if (file.getName().endsWith(".class")) { classs.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6))); } } return classs; } 

Quindi puoi semplicemente filtrare su quelle classi con l’annotazione data:

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

Probabilmente dovresti dare un’occhiata alla libreria Reflections open source. Con esso puoi facilmente ottenere ciò che vuoi con poche righe di codice:

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

Se sei felice di usare Spring, allora questo fa qualcosa seguendo queste linee usando il suo contesto: funzionalità di scansione dei componenti, dove Spring esegue la scansione delle classi annotate in un dato pacchetto. Sotto le copertine, è piuttosto raccapricciante e coinvolge l’estirpazione sul file system e nei file JAR alla ricerca di classi nel pacchetto.

Anche se non puoi usare direttamente Spring, dare un’occhiata al suo codice sorgente potrebbe darti qualche idea.

Certamente, l’APi di riflessione Java non è utile qui, in particolare non fornisce un mezzo per ottenere tutte le classi in un pacchetto.