如果一个方法有参数,为什么 Groovy 应用两次 AST 转换?

Why would Groovy apply an AST transformation twice if a method has parameters?

我是 运行 使用 Groovy/Grails v5 将 AST 转换应用于控制器方法的小测试。 该方法采用单个参数来表示典型的命令对象。 我在 AST 转换中添加了一些日志记录 class 所以在 visit() 方法中,我可以看到它正在被应用。

如果控制器方法没有参数,那么我看到正在应用转换,没问题。 但是,如果我将参数、命令对象添加到方法中,那么我会看到我的日志输出两次。

第一次应用它时,我可以看到预期的输出,告诉我它正在应用于方法,并且检测到参数。

然后第二次调用它并告诉我它正在应用,但这次它没有看到任何参数。

这是 visit() 方法:

@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
public class AjaxResponseASTTransformation extends AbstractAstTransformation {

  private static final Logger LOG = LoggerFactory.getLogger(AjaxResponseASTTransformation.class);

  @Override
  public void visit(ASTNode[] nodes, SourceUnit source) {

    final MethodNode methodNode = (MethodNode) nodes[1];
    log("detected AjaxResponse annotation for method " + methodNode.getName());

    final Parameter[] parameters = methodNode.getParameters();

    if (parameters != null) {
      for (Parameter parameter : parameters) {
        log("Found Parameter : " + parameter.getName());
      }
      if (parameters.length == 0) {
        log("No parameters present - what gives??");
      }
    }
  }

这是控制器方法:

 @AjaxResponse
    def index(MyCommand myCommand) { render "" }

构建此项目会产生以下输出:

20:01:00.537 [/127.0.0.1:51713 to /127.0.0.1:51712 workers] INFO mypackage.AjaxResponseASTTransformation - detected AjaxResponse annotation for method index
20:01:00.538 [/127.0.0.1:51713 to /127.0.0.1:51712 workers] INFO mypackage.AjaxResponseASTTransformation - Found Parameter : myCommand
20:01:00.538 [/127.0.0.1:51713 to /127.0.0.1:51712 workers] INFO mypackage.AjaxResponseASTTransformation - detected AjaxResponse annotation for method index
20:01:00.538 [/127.0.0.1:51713 to /127.0.0.1:51712 workers] INFO mypackage.AjaxResponseASTTransformation - No parameters present - what gives??

任何人都可以解释为什么它会被应用两次吗?

当你编写这样的控制器操作时:

def index(MyCommand myCommand) { 
  render "" 
}

在编译时,我们生成该方法的 no-arg 版本,如下所示:

def index() { 
  MyCommand c = new MyCommand()
  bindData c, request
  // subject c to dependency injection here, code ommitted

  // this call to validate is only generated if `MyCommand` is validateable...
  c.validate()

  // this will call your method
  index(c)
}

因此,class 中有 2 个名为 index 的方法,即使您只写了一个。

grails-plugin-controllers/src/main/groovy/org/grails/compiler/web/ControllerActionTransformer.java#L395 处是控制器动作转换器的一部分,它将您的注释复制到 no-arg 方法。

顺便说一下,这就是控制器操作方法不支持方法重载的原因。我们必须生成具有相同名称的 no-arg,因此您不能有 2 个具有相同名称但接受不同参数的操作方法。我们生成一个编译器错误,指示遇到该场景时。