Home » Java » Java – Register all classes annotated with @MyAnnotation

Java – Register all classes annotated with @MyAnnotation

Posted by: admin December 28, 2021 Leave a comment

Questions:

I have an annotation @MyAnnotation and I can annotate any type (class) with it. Then I have a class called AnnotatedClassRegister and I would like it to register all classes annotated with @MyAnnotation so I can access them later. And I’d like to register these classes automatically upon creation of the AnnotatedClassRegister if possible, and most importantly before the annotated classes are instantiated.

I have AspectJ and Guice at my disposal. The only solution I came up with so far is to use Guice to inject a singleton instance of the AnnotatedClassRegister to an aspect, which searches for all classes annotated with @MyAnnotation and it adds the code needed to register such class in its constructor. The downside of this solution is that I need to instantiate every annotated class in order for the code added by AOP to be actually run, therefore I cannot utilize lazy instantiation of these classes.

Simplified pseudo-code example of my solution:

// This is the class where annotated types are registered
public class AnnotatedClassRegister {
    public void registerClass(Class<?> clz) {
        ...
    }
}

// This is the aspect which adds registration code to constructors of annotated
// classes
public aspect AutomaticRegistrationAspect {

    @Inject
    AnnotatedClassRegister register;

    pointcutWhichPicksConstructorsOfAnnotatedClasses(Object annotatedType) : 
            execution(/* Pointcut definition */) && args(this)

    after(Object annotatedType) :
            pointcutWhichPicksConstructorsOfAnnotatedClasses(annotatedType) {

        // registering the class of object whose constructor was picked 
        // by the pointcut
        register.registerClass(annotatedType.getClass())
    }
}

What approach should I use to address this problem? Is there any simple way to get all such annotated classes in classpath via reflection so I wouldn’t need to use AOP at all? Or any other solution?

Any ideas are much appreciated, thanks!

Answers:

It’s possible:

  1. Get all paths in a classpath. Parse System.getProperties().getProperty("java.class.path", null) to get all paths.

  2. Use ClassLoader.getResources(path) to get all resources and check for classes: http://snippets.dzone.com/posts/show/4831

###

It isn’t simple that much is sure, but I’d do it in a Pure Java way:

  • Get your application’s Jar location from the classpath
  • Create a JarFile object with this location, iterate over the entries
  • for every entry that ends with .class do a Class.forName() to get the Class object
  • read the annotation by reflection. If it’s present, store the class in a List or Set

Aspects won’t help you there, because aspects only work on code that’s actually executed.

But annotation processing may be an Option, create a Processor that records all annotated classes and creates a class that provides a List of these classes

###

Well, if your AnnotatedClassRegister.registerClass() doesn’t have to be called immediately at AnnotatedClassRegister creation time, but it could wait until a class is first instantiated, then I would consider using a Guice TypeListener, registered with a Matcher that checks if a class is annotated with @MyAnnotation.

That way, you don’t need to search for all those classes, they will be registered just before being used. Note that this will work only for classes that get instantiated by Guice.

###

I would use the staticinitialization() pointcut in AspectJ and amend classes to your register as they are loaded, like so:

after() : staticinitialization(@MyAnnotation *) {
    register.registerClass(thisJoinPointStaticPart.getSignature().getDeclaringType());
}

Piece of cake, very simple and elegant.

###

You can use the ClassGraph package like so:

Java:

try (ScanResult scanResult = new ClassGraph().enableAnnotationInfo().scan()) {
  for (ClassInfo classInfo = scanResult.getClassesWithAnnotation(classOf[MyAnnotation].getName()) {
    System.out.println(String.format("classInfo = %s", classInfo.getName()));
  }
}

Scala:

Using(new ClassGraph().enableAnnotationInfo.scan) { scanResult =>
  for (classInfo <- scanResult.getClassesWithAnnotation(classOf[MyAnnotation].getName).asScala) {
    println(s"classInfo = ${classInfo.getName}")
  }
}