如何获取方法的注释

时间:2014-03-26 08:04:47

标签: scala annotations

我们假设我们有一个像这样的 scala 类:

class MyClass {

  @MyAnnotationWithParams(message = "Hello", lang = "en-US")
  def myMethod1 = println("Hello everybody")

  @MyAnnotationWithParams(message = "Hallo", lang = "de-DE")
  def myMethod2 = println("Hallo zama")
}

如何在运行时获取每个方法的注释值?

1 个答案:

答案 0 :(得分:4)

以下函数将从方法名称返回到从注释名称到注释数据的映射。每个注释数据都是注释参数名称和参数值的映射。

import scala.reflect.runtime.universe._

def methodAnnotations[T: TypeTag]: Map[String, Map[String, Map[String, JavaArgument]]] = {
  val tpe = typeTag[T].tpe
  val methods = tpe.declarations.collect { case m: MethodSymbol => m }
  methods.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
}

让我们检查它的工作原理:

scala> import javax.xml.bind.annotation.XmlNs
import javax.xml.bind.annotation.XmlNs

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Example {
  @SuppressWarnings(Array("unchecked"))
  def test1() {}

  @XmlNs(prefix="c", namespaceURI="urn:abcd")
  def test2() {}
}

// Exiting paste mode, now interpreting.

defined class Example

scala> methodAnnotations[Example]
res50: Map[String,Map[String,Map[String,reflect.runtime.universe.JavaArgument]]] = Map(<init> -> Map(), test1 -> Map(SuppressWarnings -> Map(value -> ["unchecked"])), test2 -> Map(XmlNs -> Map(prefix -> "c", namespaceURI -> "urn:abcd")))

您可以看到地图已正确构建。从JavaArgument类中提取实际值需要更多操作。 JavaArgument是一个抽象类,它有几个子类,例如LiteralArgumentArrayArgument。您必须手动转换获得的值,具体取决于您正在使用的实际注释:

scala> res50("test1")("SuppressWarnings")("value").asInstanceOf[ArrayArgument]
res51: reflect.runtime.universe.ArrayArgument = ["unchecked"]

scala> res51.args.collect { case a: LiteralArgument => a.value.value }.collect { case a: String => a }
res58: Array[String] = Array(unchecked)

scala> res50("test2")("XmlNs")("prefix").asInstanceOf[LiteralArgument].value.value.asInstanceOf[String]
res59: String = c

这是通过Scala反射处理注释的正确方法(可能你甚至应该使用scalaArgs而不是javaArgs;这将提供与我在这里使用的结构不同的结构。 / p>

有一种更简单但不正确的方法。您可以使用Java反射:

import scala.reflect.{ClassTag, classTag}
import java.lang.annotation.{Annotation => JAnnotation}

def javaMethodAnnotations[T: ClassTag]: Map[String, List[JAnnotation]] = {
  classTag[T].runtimeClass.getDeclaredMethods.map { m =>
    m.getName -> m.getAnnotations.toList
  }.toMap
}

使用示例:

scala> javaMethodAnnotations[Example]
res71: Map[String,List[java.lang.annotation.Annotation]] = Map(test1 -> List(), test2 -> List(@javax.xml.bind.annotation.XmlNs(prefix=c, namespaceURI=urn:abcd)))

请注意,@SuppressWarnings注释缺失。那是因为它的保留不是运行时。但这通常不是问题。

这样您就必须将注释对象转换为注释类型:

scala> res71("test2")(0).asInstanceOf[XmlNs].prefix()
res72: String = c

有趣的链接: