如何在Grails插件中实现Groovy全局AST转换?

时间:2014-11-24 22:22:46

标签: grails groovy grails-plugin

我想在编译时修改我的一些Grails域类。我最初认为这是Groovy全球ASTTransformation的工作,因为我不知道想要注释我的域类(本地变换器需要)。最好的方法是什么?

我也尝试通过在同一个包中创建我自己的类来模仿 DefaultGrailsDomainClassInjector.java ,实现相同的接口,但我可能只是不知道如何在右边打包它因为我从来没有看到我的方法被调用过。

另一方面,我能够手动创建一个包含已编译的AST转换类的JAR,以及纯Groovy全局转换所需的META-INF / services工件。我把那个JAR扔进了我的项目" lib" dir和visit()已成功调用。显然这是一个草率的工作,因为我希望在Grails插件中获得我的AST转换的源代码,如果我不需要,则不需要单独的JAR工件,而且我无法获得这种方法通过在我的Grails插件中使用JAR来进行工作" lib"但不得不把它放到Grails app" lib"代替。

这篇文章也有所帮助:Grails 2.1.1 - How to develop a plugin with an AstTransformer?

1 个答案:

答案 0 :(得分:5)

关于全局变换的事情,编译开始时变换代码应该可用。将变压器放在罐子里是我先做的!但正如你所说,这是一项草率的工作。 你想要做的是让你的ast转换类在其他人进入编译阶段之前编译。这就是你做的!

准备变压器

precompiled文件夹中创建名为src的目录!并添加Transformation类和变换器在此目录中使用的类(如注释)以及正确的打包结构。

然后在被叫org.codehaus.groovy.transform.ASTTransformation中创建一个名为precompiled/META-INF/services的文件,您将拥有以下结构。

precompiled
--amanu
----LoggingASTTransformation.groovy
--META-INF
----services
------org.codehaus.groovy.transform.ASTTransformation

然后在org.codehaus.groovy.transform.ASTTransformation文件中写入变换器的完全限定名称,对于上面的示例,完全限定名称为amanu.LoggingASTTransformation

实施

package amanu 

import org.codehaus.groovy.transform.GroovyASTTransformation 
import org.codehaus.groovy.transform.ASTTransformation 
import org.codehaus.groovy.control.CompilePhase 
import org.codehaus.groovy.ast.ASTNode 
import org.codehaus.groovy.control.SourceUnit 

@GroovyASTTransformation(phase=CompilePhase.CANONICALIZATION) 
class TeamDomainASTTransformation implements ASTTransformation{ 

   public void visit(ASTNode[] nodes, SourceUnit sourceUnit) { 
       println ("*********************** VISIT ************")
       source.getAST()?.getClasses()?.each { classNode -> 
          //Class node is a class that is contained in the file being compiled
          classNode.addProperty("filed", ClassNode.ACC_PUBLIC, new ClassNode(Class.forName("java.lang.String")), null, null, null)
       }
   } 
} 

汇编

执行此操作后,您可以通过两种方式进行操作!第一种方法是将它放在罐子里,就像你一样!另一种是使用groovy脚本在其他人之前编译它。要在grails中执行此操作,我们使用_Events.groovy脚本。

您可以从插件或主项目中执行此操作,这无关紧要。如果它不存在,请创建一个名为_Events.groovy的文件并添加以下内容。

代码是从reinhard-seiler.blogspot.com复制而来的

eventCompileStart = {target ->  
    ...
    compileAST(pluginBasedir, classesDirPath)  
    ...
  }  
  def compileAST(def srcBaseDir, def destDir) {  
   ant.sequential {  
      echo "Precompiling AST Transformations ..."  
      echo "src ${srcBaseDir} ${destDir}"  
      path id: "grails.compile.classpath", compileClasspath  
      def classpathId = "grails.compile.classpath"  
      mkdir dir: destDir  
      groovyc(destdir: destDir,  
          srcDir: "$srcBaseDir/src/precompiled",  
          classpathref: classpathId,  
          stacktrace: "yes",  
          encoding: "UTF-8")
     copy(toDir:"$destDir/META-INF"){
        fileset(dir:"$srcBaseDir/src/precompiled/META-INF")
     }  
      echo "done precompiling AST Transformations"  
    }  
}  

之前的脚本将在编译其他脚本之前编译变换器!这使变换器可用于转换域类。

别忘了

如果您使用除类路径中添加的类以外的任何类,则还必须预编译它们。上面的脚本将编译precompiled目录中的所有内容,你也可以添加不需要ast的类,但在该目录中需要它!

如果要在转换中使用域类,可能需要在evenCompileEnd块中进行预编译!但这会让事情变得更慢!

更新

@Douglas Mendes提到有一种简单的方法可以导致预编译。哪个更简洁。

eventCompileStart = { 
   target -> projectCompiler.srcDirectories.add(0, "./src/precompiled") 
}