How to get the annotations of a method in Scala 2.11

大憨熊 提交于 2020-01-13 07:08:09

问题


Let's assume a controller object like this:

object Users extends Controller {

  ...

  @ApiOperation(
    httpMethod = "POST",
    nickname = "authenticate",
    value = "Authenticates an user",
    notes = "Returns the JSON Web Token to be used in any subsequent request",
    response = classOf[models.auth.api.Jwt])
  def authenticate = SecuredAction[Users.type]("authenticate").async(parse.json) { implicit request =>
    ...
  }

  ...
}

How do I get the annotation values of the authenticate method at runtime? I've tried this:

def methodAnnotations[T: TypeTag]: Map[String, Map[String, Map[String, JavaArgument]]] = {
  typeTag[T].tpe.declarations.collect { case m: MethodSymbol => m }.map { m =>
    val methodName = m.name.toString
    val annotations =  m.annotations.map { a =>
      val annotationName = a.tpe.typeSymbol.name.toString
      val annotationArgs = a.javaArgs.map {
        case (name, value) => name.toString -> value
      }
      annotationName -> annotationArgs
    }.toMap
    methodName -> annotations
  }.toMap
}   

methodAnnotations returns the specified annotation for the specified method and is invoke like this:

val mAnnotations = methodAnnotations[T]
val nickname = mAnnotations("myMethodName")("MyAnnotationName")("myAnnotationMemberName").asInstanceOf[LiteralArgument].value.value.asInstanceOf[String]

The problem is that when I compile the code above I always get the following warnings:

type JavaArgument in trait Annotations is deprecated: Use `Annotation.tree` to inspect annotation arguments
method tpe in trait AnnotationApi is deprecated: Use `tree.tpe` instead

What's the correct way to get method annotations with scala 2.11?


回答1:


If you can handle using Jackson, then I'd re-use its annotation processing functionality instead of using scala reflection.

object Test {
  import com.fasterxml.jackson.databind.introspect.{AnnotatedClass, JacksonAnnotationIntrospector}

  @ApiOperation(
    httpMethod = "POST",
    nickname = "authenticate",
    value = "Authenticates an user",
    notes = "Returns the JSON Web Token to be used in any subsequent request",
    response = classOf[models.auth.api.Jwt])
  def hasAnnotation() {}

  def main(args: Array[String]): Unit = {
    import scala.collection.JavaConversions._

    val introspector = new JacksonAnnotationIntrospector
    val ac = AnnotatedClass.construct(Test.getClass, introspector, null)
    for (method <- ac.memberMethods()) {
      val annotation = method.getAnnotation(classOf[ApiOperation])
      if (annotation != null) {
        println(s"${method.getFullName} -> ${annotation.nickname()}")
      }
    }
  }
}



回答2:


Probably the solution suggested by Nate is the most clean and efficient... but in my case it would require too much refactoring, so I've just decided to still use Scala reflection. Here is my solution:

package utils.common

import scala.reflect.runtime.universe._

/**
  * Provides functionality for obtaining reflective information about
  * classes and objects.
  */
object ReflectHelper {

  /**
    * Returns a `Map` from annotation names to annotation data for
    * the specified type.
    *
    * @tparam T The type to get class annotations for.
    * @return The class annotations for `T`.
    */
  def classAnnotations[T: TypeTag]: Map[String, Map[String, Any]] = {
    typeOf[T].typeSymbol.asClass.annotations.map { a =>
      a.tree.tpe.typeSymbol.name.toString -> a.tree.children.withFilter {
        _.productPrefix eq "AssignOrNamedArg"
      }.map { tree =>
        tree.productElement(0).toString -> tree.productElement(1)
      }.toMap
    }.toMap
  }

  /**
    * Returns a `Map` from method names to a `Map` from annotation names to
    * annotation data for the specified type.
    *
    * @tparam T The type to get method annotations for.
    * @return The method annotations for `T`.
    */
  def methodAnnotations[T: TypeTag]: Map[String, Map[String, Map[String, Any]]] = {
    typeOf[T].decls.collect { case m: MethodSymbol => m }.withFilter {
      _.annotations.length > 0
    }.map { m =>
      m.name.toString -> m.annotations.map { a =>
        a.tree.tpe.typeSymbol.name.toString -> a.tree.children.withFilter {
         _.productPrefix eq "AssignOrNamedArg"
        }.map { tree =>
          tree.productElement(0).toString -> tree.productElement(1)
        }.toMap
      }.toMap
    }.toMap
  }
}

I hope it helps.



来源:https://stackoverflow.com/questions/26897279/how-to-get-the-annotations-of-a-method-in-scala-2-11

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!