3

I am new to Kotlin and I want to do the following:

  1. Annotate some functions with an annotation e.g "Executable"

  2. At runtime, get all the functions with this annotation

  3. Inspect a property on the annotation and if it matches a condition, invoke the function

I have the following code

annotation class Executable(val name : String)

@Executable("doSomething")
fun stepDoSomething (param1 : String) {
    println("I am a step that does something! I print $param1")
}

However, I am unclear on how to retrieve all functions with the Executable annotation at runtime and inspect them.

Thank you for your help!

2 Answers2

1

To accomplish this, you will need to use a classpath scanner, such as ClassGraph. Classpath scanners offer APIs to find classes based on various criteria, such as what package they’re in, what interface they implement, or what annotations they have. In the case of ClassGraph, the ScanResult has a getClassesWithMethodAnnotation(String name) method. Once you have all of those classes, you can use ordinary reflection to find which method(s) in those classes have the specific annotation you’re looking for and inspect the properties of the annotations. Here is a good overview of how to create an annotation and inspect it using reflection.

Matthew Pope
  • 7,212
  • 1
  • 28
  • 49
0

Here is my implementation based on (very helpful) Matthew Pope's answer:

import io.github.classgraph.ClassGraph
import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.jvm.kotlinFunction

@Image(filename = "image-1.svg")
fun foo() {
    println("in foo")
}

@Image(filename = "image-2.svg")
fun bar() {
    println("in bar")
}

@Throws(Exception::class)
fun getAllAnnotatedWith(annotation: KClass<out Annotation>): List<KFunction<*>> {
    val `package` = annotation.java.`package`.name
    val annotationName = annotation.java.canonicalName

    return ClassGraph()
        .enableAllInfo()
        .acceptPackages(`package`)
        .scan().use { scanResult ->
            scanResult.getClassesWithMethodAnnotation(annotationName).flatMap { routeClassInfo ->
                routeClassInfo.methodInfo.filter{ function ->
                     function.hasAnnotation(annotation.java) }.mapNotNull { method ->
                        method.loadClassAndGetMethod().kotlinFunction
                        // if parameter needed:
                        // method.getAnnotationInfo(routeAnnotation).parameterValues.map { it.value }
                    }
            }
        }
}

fun main(args: Array<String>) {
    getAllAnnotatedWith(Image::class)
        .forEach { function ->
            function.call()
        }
}
Dimitry Ernot
  • 6,256
  • 2
  • 25
  • 37