我们假设我们有一个像这样的 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")
}
如何在运行时获取每个方法的注释值?
答案 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
是一个抽象类,它有几个子类,例如LiteralArgument
或ArrayArgument
。您必须手动转换获得的值,具体取决于您正在使用的实际注释:
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
有趣的链接: