I have a simple marker annotation for methods (similar to the first example in Item 35 in Effective Java (2nd ed)):
/** * Marker annotation for methods that are called from installer's * validation scripts etc. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface InstallerMethod { }
Then, in a given package (say com.acme.installer
), which has a few subpackages containing some 20 classes, I'd like to find all methods that are annotated with it. (Because I'd like to do some checks regarding all the annotated methods in a unit test.)
What (if any) is the easiest way to do this? Preferably without adding new 3rd party libraries or frameworks.
Edit: to clarify, obviously method.isAnnotationPresent(InstallerMethod.class)
will be the way to check if a method has the annotation - but this problem includes finding all the methods.
The isAnnotation() method is used to check whether a class object is an annotation. The isAnnotation() method has no parameters and returns a boolean value. If the return value is true , then the class object is an annotation. If the return value is false , then the class object is not an annotation.
Spring has something called a AnnotatedTypeScanner class. This class has the code for actual scanning of the classpath resources. It does this by using the class metadata available at runtime.
An annotation is a construct associated with Java source code elements such as classes, methods, and variables. Annotations provide information to a program at compile time or at runtime based on which the program can take further action.
Try: CTRL-H -> File Search, check Case Sensitive and Regular expression, and input: @[A-Z] for Containing text.
If you want to implement it yourself, these methods will find all the classes in a given package:
/** * 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; }
Then you can just filter on those classes with the given annotation:
for (Method method : testClass.getMethods()) { if (method.isAnnotationPresent(InstallerMethod.class)) { // do something } }
You should probably take a look at the open source Reflections library. With it you can easily achieve what you want with few lines of code:
Reflections reflections = new Reflections( new ConfigurationBuilder().setUrls( ClasspathHelper.forPackage( "com.acme.installer" ) ).setScanners( new MethodAnnotationsScanner() ) ); Set<Method> methods = reflections.getMethodsAnnotatedWith(InstallerMethod.class);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With