In Java
programming language, annotation
offers a super way for supplying meta information about our code. These meta information can be used by frameworks to decide the functionality of that particular feature. When you design a module or a feature, you can define your own annotations and your feature can use it to achieve the functionalities similar to the existing frameworks
The information in the annotaion is made available to the runtime of the code in 2 ways.
- Using
annotation processor
. Annotation Processors are called by java compiler to process the information given at the AnnotatedElements to generate new Java source files or other meta-data - Scanning the annotations during the JVM startup and using Java reflection, the
AnnotatedElement
s can be identified and we can create meta models accordingly.
This article deals with the 2nd way of scanning the annotated classes from the classpath or methods or variables in the runtime and using it for our feature.
There are libraries like classgraph that can be used in java applications that do not use spring framework
. If your application do not use spring framework or spring boot, you can refer to the classgraph library documentation and proceed.
If your application uses spring framework, then you can use the below snippet and get the list of classes that have been annotated with a particular annotation
If your custom annotation is @MyCustomAnnotation
, you can find all the classes which are annotated with annotation @MyCustomAnnotation
using the below function call
findAnnotatedClasses(MyCustomAnnotation.class, "com.abc.def.xyz")
The packages list is given as varargs as it would enable neat code in the calling places.
The implementation of findAnnotatedClasses()
method is given below. The class ClassPathScanningCandidateComponentProvider
of spring framework does the magic of scanning the entire classpath and returns us the Bean classes which have the annotation @MyCustomAnnotation.
For spring framework to scan your class that has your custom annotation @MyCustomAnnotation,
your class must be annotated with @Component
or the other spring annotations that make your class a managed component of Spring.
public Set<String> findAnnotatedClasses(Class<? extends Annotation> annotationType, String... packagesToBeScanned)
{
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new
AnnotationTypeFilter(annotationType));
Set<String> ret = new HashSet<>();
for (String pkg : packagesToBeScanned)
{
Set<BeanDefinition> beanDefs = provider.findCandidateComponents(pkg);
beanDefs.stream()
.map(BeanDefinition::getBeanClassName)
.forEach(ret::add);
}
return ret;
}
Spring Application's main class below shows how to call this method.
@SpringBootApplication
public class SpringApp implements CommandLineRunner
{
@Autowired
private ClassScanner classScanner;
public static void main(String[] args)
{
SpringApplication.run(SpringApp.class, args);
}
@Override
public void run(String... args) throws Exception
{
Set<String> annotatedClasses = classScanner.findAnnotatedClasses(MyCustomAnnotation.class, "xyz.reachadi");
for (String c : annotatedClasses)
System.out.println("Annotated Class : " + c);
}
}
The approach is very simple..... right?
So, what next? Enhance your codes' readability by defining custom annotations and using them on your codes.
The full working example of this blog is available in my github repository.
Please refer to readme.md
for instructions on running the tutorial.
Top comments (3)
Run-time reflection and class path scanning are great ways to make Java apps slow, memory hungry and taking ages to start.
True. But, based on the application and the scope of scanning, developers can make a tradeoff to utilize this annotation based design....!
I think that run-time reflection should be avoided as much as possible. Just take a look at Micronaut - same convenience and flexibility and no drawbacks of run-time reflection.