scala.reflect.api.Annotations 的 Scaladoc 说
与 Java 反射不同,Scala 反射不支持求值
存储在注释中的构造函数调用到底层
对象。例如,不可能从 @ann(1, 2) class C 转到
ann(1, 2),所以必须分析表示注释的树
手动提取相应值的参数。为此,
注解的参数可以通过annotation.tree.children.tail获取。
https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/api/Annotations.scala#L39-L42
您可以使用Toolbox 评估注释树
// libraryDependencies += scalaOrganization.value % "scala-compiler" % scalaVersion.value
import scala.tools.reflect.ToolBox
val rm = u.runtimeMirror(handlerDef.classLoader)
val tb = rm.mkToolBox()
res.map(a => tb.eval(tb.untypecheck(a.tree)).asInstanceOf[Auth]) // Some(Auth(some permission))
https://docs.scala-lang.org/overviews/reflection/symbols-trees-types.html#tree-creation-via-parse-on-toolboxes
Calling a method from Annotation using reflection
或者(例如,如果您只想依赖 scala-reflect 而不是 scala-compiler)您可以手动评估树:
res.map(a => {
val arg = a.tree.children.tail match { case List(q"${s: String}") => s }
Auth(arg)
}) // Some(Auth(some permission))
或
res.map(_.tree match {
case q"new ${t@TypeTree()}(${s: String})" /*if t.tpe == typeOf[Auth]*/ => Auth(s)
}) // Some(Auth(some permission))
请注意,树将不匹配模式q"new com.example.Auth(${s: String})",因为注释树具有不同的形状。
顺便说一句,使用rm.staticClass(...) 代替rm.classSymbol(Class.forName(...)),您可以使用Scala 类名(例如org.example.App.MyClass)代替Java 类名(例如org.example.App$MyClass)。您也可以尝试scala.reflect.runtime.currentMirror 而不是u.runtimeMirror(classLoader)。另外.decls.find(_.name.toString == methodName) 可以替换为.decl(u.TermName(methodName))。
以防万一,如果您在编译时知道类和注解的类型以及方法名称,那么您可以使用编译时反射来做同样的事情
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
def getMethodAnnotation[Cls, Ann](methodName: String): Ann = macro getMethodAnnotationImpl[Cls, Ann]
def getMethodAnnotationImpl[Cls: c.WeakTypeTag, Ann: c.WeakTypeTag](c: blackbox.Context)(methodName: c.Tree): c.Tree = {
import c.universe._
val q"${methodNameStr: String}" = methodName
weakTypeOf[Cls]
.decl(TermName(methodNameStr))
.annotations.find(_.tree.tpe =:= weakTypeOf[Ann]).get.tree match {
case q"new ${t: TypeTree}[..$targs](...$argss)" => q"new ${t.tpe}[..$targs](...$argss)"
}
}
class MyClass {
@Auth("some permission")
def myMethod(): Unit = ()
}
getMethodAnnotation[MyClass, Auth]("myMethod") //scalac: new Auth("some permission")