【问题标题】:How to get the annotations of a method in Scala 2.11如何在 Scala 2.11 中获取方法的注释
【发布时间】:2014-11-12 21:48:53
【问题描述】:

让我们假设一个控制器对象是这样的:

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 =>
    ...
  }

  ...
}

如何在运行时获取authenticate 方法的注解值?我试过这个:

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 返回指定方法的指定注解,调用方式如下:

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

问题是,当我编译上面的代码时,我总是收到以下警告:

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

使用 scala 2.11 获取方法注释的正确方法是什么?

【问题讨论】:

    标签: scala reflection annotations scala-2.11


    【解决方案1】:

    如果您可以使用 Jackson 来处理,那么我会重新使用它的注释处理功能,而不是使用 scala 反射。

    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()}")
          }
        }
      }
    }
    

    【讨论】:

    • 我会尝试...但我可以问一下为什么杰克逊比 scala 反射更好?只是被告知;-)
    • @j3d Jackson 被广泛使用、健壮、快速和干净。那么......如果它可以轻松完成工作,为什么不呢=)?此外,Scala 反射 API 不允许您在不窥视的情况下访问注释参数(请参阅:stackoverflow.com/questions/26635223/…)。
    • 我试过了...但它对我不起作用,因为SecuredAction 必须采用类型参数...并且传递的类型必须是从Controller 派生的object ...
    • 我不确定我是否看到了连接。您的问题根本不使用 SecuredAction,除非在您的方法定义中(不会公开)。您是不是在尝试检索“身份验证”方法上的注释,还是在尝试获取其他内容?
    • SecuredAction 是一种自定义操作,旨在根据当前授权配置文件保护控制器方法。 SecuredAction 是通用的,被许多不同的控制器使用。 SecuredAction 自动读取方法昵称并确定当前用户是否有权调用该方法(对于每个昵称,配置中都有一个条目,指定谁可以调用与该昵称关联的方法)。
    【解决方案2】:

    Nate 建议的解决方案可能是最干净和最有效的......但在我的情况下,它需要太多的重构,所以我决定仍然使用 Scala 反射。这是我的解决方案:

    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
      }
    }
    

    希望对你有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-06
      • 1970-01-01
      • 2014-02-12
      相关资源
      最近更新 更多