Grails:为什么在编译期间执行Config.groovy文件?

use*_*748 7 configuration grails groovy log4j maven

Grails文档这个问题:

对于一般配置,Grails提供了两个文件:

  1. 的grails-app/CONF/BuildConfig.groovy
  2. 的grails-app/CONF/Config.groovy中

它们都使用Groovy的ConfigSlurper语法.第一个是BuildConfig.groovy,用于运行Grails命令时使用的设置,例如compile,doc等.第二个文件Config.groovy用于运行应用程序时使用的设置.这意味着Config.groovy与您的应用程序一起打包,但BuildConfig.groovy不是.

这是关于log4j框架的摘录:

Grails使用其通用配置机制为底层Log4j日志系统提供设置,因此您所要做的就是将log4j设置添加到文件grails-app/conf/Config.groovy.

我有一个包含这两个文件的项目:grails-app/conf/BuildConfig.groovygrails-app/conf/Config.groovy.该项目使用该log4j实用程序,因此一些设置(包括函数)放在Config.groovy文件中.现在,根据我引用的文档,它是正确的文件,因为我想使用日志实用程序来运行应用程序,而不是编译它.

有趣的是,当我mvn package在项目上运行时会使用这些设置- log4j执行设置中的功能.

根据文件问题,情况并非如此.

我知道可以使用它grailsApplication来访问这些设置:

def recipient = grailsApplication.config.foo.bar.hello
Run Code Online (Sandbox Code Playgroud)

所以我搜索了我的项目并找到了一些用法grailsApplication,但没有与log4j设置有关.

使用在Config.groovy文件中放置log4j设置的其他可能原因是什么mvn package?我错过了什么?

更新:当我mvn package第一次使用构建项目时,提到的配置似乎有效.下次运行时mvn package,将使用文件中的log4j配置Config.groovy.如果我删除了工作空间,它再次运行良好.

Szy*_*iak 4

有一件重要的事情值得一提。即使Config.groovy应用程序在运行时使用它,它也必须Config.class首先编译为 Java。当您打包应用程序时,编译器必须访问该文件并将其编译为字节码。看一下我在下面粘贴的列表,它来自我工作区中的一个 Grails 应用程序:

命令:

ls -l target/classes | awk '{print $8}'
Run Code Online (Sandbox Code Playgroud)

输出:

application.properties
BootStrap.class
BootStrap$_closure1.class
BootStrap$_closure2.class
BuildConfig.class
BuildConfig$_run_closure1.class
BuildConfig$_run_closure1_closure2.class
BuildConfig$_run_closure1_closure3.class
BuildConfig$_run_closure1_closure4.class
BuildConfig$_run_closure1_closure5.class
com
Config.class
Config$_run_closure1.class
Config$_run_closure1_closure4.class
Config$_run_closure1_closure4_closure5.class
Config$_run_closure1_closure4_closure5_closure6.class
Config$_run_closure2.class
Config$_run_closure2_closure7.class
Config$_run_closure2_closure8.class
Config$_run_closure3.class
DataSource.class
DataSource$_run_closure1.class
DataSource$_run_closure2.class
DataSource$_run_closure3.class
DataSource$_run_closure3_closure4.class
DataSource$_run_closure3_closure4_closure7.class
DataSource$_run_closure3_closure5.class
DataSource$_run_closure3_closure5_closure8.class
DataSource$_run_closure3_closure6.class
DataSource$_run_closure3_closure6_closure9.class
DataSource$_run_closure3_closure6_closure9_closure10.class
resources.class
resources$_run_closure1.class
UrlMappings.class
UrlMappings$__clinit__closure1.class
UrlMappings$__clinit__closure1_closure2.class
UrlMappings$__clinit__closure1_closure2_closure3.class
Run Code Online (Sandbox Code Playgroud)

您可以看到Config.groovyfile 被编译为Config.classfile 并且内部使用的所有闭包都Config.groovy被编译为 Java 的匿名类(例如Config$_run_closure1_closure4.class)。这就是为什么如果您放置一些执行任何逻辑的代码,Config.groovy您必须期望它会被编译并执行,因为编译后的类扩展groovy.lang.Script并且它执行 Groovy 脚本文件的主体。您可以在下面找到Groovy.class文件的样子:

命令:

javap -l  target/classes/Config.class
Run Code Online (Sandbox Code Playgroud)

输出:

Compiled from "Config.groovy"
public class Config extends groovy.lang.Script {
  public static transient boolean __$stMC;

  public static long __timeStamp;

  public static long __timeStamp__239_neverHappen1501076781354;

  public Config();
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          4       4     0  this   LConfig;

  public Config(groovy.lang.Binding);
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          4      21     0  this   LConfig;
          4      21     1 context   Lgroovy/lang/Binding;

  public static void main(java.lang.String...);
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      19     0  args   [Ljava/lang/String;

  public java.lang.Object run();
    LineNumberTable:
      line 14: 4
      line 17: 43
      line 18: 126
      line 24: 194
      line 26: 233
      line 30: 296
      line 31: 323
      line 38: 376
      line 42: 419
      line 45: 453
      line 63: 473
      line 65: 507
      line 68: 550
      line 70: 595
      line 72: 631
      line 74: 679
      line 77: 724
      line 80: 777
      line 84: 822
      line 86: 867
      line 88: 912
      line 99: 932
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0     964     0  this   LConfig;

  public java.lang.Object this$dist$invoke$3(java.lang.String, java.lang.Object);
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      68     0  this   LConfig;
          0      68     1  name   Ljava/lang/String;
          0      68     2  args   Ljava/lang/Object;

  public void this$dist$set$3(java.lang.String, java.lang.Object);
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      53     0  this   LConfig;
          0      53     1  name   Ljava/lang/String;
          0      53     2 value   Ljava/lang/Object;

  public java.lang.Object this$dist$get$3(java.lang.String);
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      46     0  this   LConfig;
          0      46     1  name   Ljava/lang/String;

  protected groovy.lang.MetaClass $getStaticMetaClass();

  public static void __$swapInit();

  static {};

  public int super$1$hashCode();

  public void super$3$printf(java.lang.String, java.lang.Object);

  public void super$3$printf(java.lang.String, java.lang.Object[]);

  public void super$3$setProperty(java.lang.String, java.lang.Object);

  public boolean super$1$equals(java.lang.Object);

  public void super$1$finalize();

  public groovy.lang.Binding super$3$getBinding();

  public void super$3$print(java.lang.Object);

  public void super$3$setBinding(groovy.lang.Binding);

  public java.lang.Object super$3$evaluate(java.io.File);

  public java.lang.String super$1$toString();

  public java.lang.Object super$3$evaluate(java.lang.String);

  public void super$2$setMetaClass(groovy.lang.MetaClass);

  public void super$1$notify();

  public java.lang.Object super$3$invokeMethod(java.lang.String, java.lang.Object);

  public java.lang.Object super$1$clone();

  public void super$1$wait(long, int);

  public void super$1$wait(long);

  public void super$1$wait();

  public groovy.lang.MetaClass super$2$getMetaClass();

  public java.lang.Class super$1$getClass();

  public void super$3$run(java.io.File, java.lang.String[]);

  public void super$3$println(java.lang.Object);

  public void super$1$notifyAll();

  public java.lang.Object super$3$getProperty(java.lang.String);

  public void super$3$println();

  static java.lang.Class class$(java.lang.String);
}
Run Code Online (Sandbox Code Playgroud)

更新

现在要理解为什么Config.groovy要执行,我们需要深入grails package研究执行时会发生什么。运行grails package命令使_GrailsPackage.groovygrails-core 中的脚本被执行。packageApp目标呼叫GrailsProjectPackager.packageApplication()

https://github.com/grails/grails-core/blob/2.4.x/grails-scripts/src/main/scripts/_GrailsPackage.groovy#L48

该方法调用createConfig()辅助类:

https://github.com/grails/grails-core/blob/2.4.x/grails-project-api/src/main/groovy/org/codehaus/groovy/grails/project/packaging/GrailsProjectPackager.groovy#L274

createConfig()方法实现中我们可以找到ConfigSlurper.parse(script)执行:

https://github.com/grails/grails-core/blob/2.4.x/grails-project-api/src/main/groovy/org/codehaus/groovy/grails/project/packaging/GrailsProjectPackager.groovy#L345

这是一个 Groovy 类,它解析脚本并最终调用script.run()解析后的脚本。使用调试器很容易找到答案 - 请查看我的视频,其中我展示了如何在这个特定示例中执行此操作:https://www.youtube.com/watch ?v=s2PN6TjFjUI

我希望它有帮助。