Search code examples
javascalareflectionannotationsreflections

How can I find Annotated methods in Scala/Java on Runtime


I want to use Runtime Reflection with Scala annotations (could also be a Java annoations if necessary, but I would prefer to limit pure Java code)

I want to implement something like:

/**
  print all methods that implement a specific annotation
*/
 def getAllAnnotated(): Unit {...}

For example, if I have:

class Foo {
    @printme
    def foo(args: A): R
    
    def oof(args: A): R
}
class Bar {
    @printme
    def bar(): Unit
}

The result of running getAllAnnotated() would be something like:

Foo.foo
Bar.bar

Note that I don't want to look in a specific class, but instead any available method


Solution

  • Try one of classpath scanners based on Java reflection (e.g. Reflections) + scala-reflect. Since we use Java reflection only to look for classes and scala-reflect to look for annotated methods, annotations can be written in Scala.

    import org.reflections.Reflections
    import org.reflections.scanners.SubTypesScanner
    import org.reflections.util.{ClasspathHelper, ConfigurationBuilder}
    import scala.annotation.StaticAnnotation
    import scala.jdk.CollectionConverters._
    import scala.reflect.runtime.currentMirror
    import scala.reflect.runtime.universe._
    
    class printme extends StaticAnnotation
    
    val reflections = new Reflections(
      (new ConfigurationBuilder)
        .setUrls(ClasspathHelper.forPackage(""))
        .setScanners(new SubTypesScanner(false))
    )
    
    def getAllAnnotated(): Unit =
      reflections.getAllTypes.asScala
        .flatMap(className =>
          currentMirror.classSymbol(Class.forName(className))
            .toType
            .decls
            .filter(symbol =>
              symbol.isMethod && symbol.annotations.exists(_.tree.tpe =:= typeOf[printme])
            )
            .map(method => s"$className.${method.name}")
        ).foreach(println)
    

    Alternatives to Reflections library are for example ClassGraph and Burningwave. If we replace scala-reflect with Java reflection then annotation will have to be written in Java because only annotations written in Java are visible at runtime with Java reflection.