创建一个运行groovy脚本的gradle任务的最佳方法是什么?我意识到gradle构建文件是常规的,所以我认为可以做这样的事情:
task run << {
Script app = new GroovyShell().parse(new File("examples/foo.groovy"))
// or replace .parse() w/ a .evalulate()?
app.run()
}
Run Code Online (Sandbox Code Playgroud)
如果bar.groovy正在使用@Grab注释甚至是简单的导入,那么当我尝试这个时,我会遇到各种各样的错误.我想创建一个任务的Gradle来处理这个问题,让我能有希望重用的类路径定义.
将examples目录移动到某个地方的src目录会更好吗?什么是最佳做法?
我正在尝试将Groovy脚本作为类动态加载,但是即使脚本的代码无法编译,也会创建类对象。
例如,用于加载Groovy脚本的我的Groovy代码的简化版本如下:
GroovyCodeSource src = new GroovyCodeSource(
"blah blah blah",
"Foo.groovy",
GroovyShell.DEFAULT_CODE_BASE
)
new GroovyClassLoader().parseClass(src, true)
Run Code Online (Sandbox Code Playgroud)
显然,该代码blah blah blah不是合法的Groovy脚本。但是,已经为该动态代码成功创建了一个类对象。根据GroovyClassLoader的Javadoc,对于这种parseClassCompilationFailedException情况,应抛出方法 a 。
如何仍然为残破的代码创建类,以及如何根据条件是否编译将如何从动态Groovy源代码成功创建类?我做了很多研究和实验,但无济于事。
我在我的Java类中使用GroovyClassLoader来解析某个(理想情况下很复杂的)groovy文件(将在下一步执行):
在MyClass.java中调用
final Class parsedClass = groovyClassLoader.parseClass(groovyFile);
Run Code Online (Sandbox Code Playgroud)
知道:
GroovyFile.groovy进口
import com.my.import.one.Import1DTO
import com.my.import.two.Import2DTO
import com.my.import.three.Import3DTO
import com.my.import.four.Import4DTO
import com.my.import.five.Import5DTO
Run Code Online (Sandbox Code Playgroud)
当调用parseClass方法时,此异常引发:
例外
unable to resolve class com.my.import.one.Import1DTO;
unable to resolve class com.my.import.two.Import2DTO;
unable to resolve class com.my.import.three.Import3DTO;
unable to resolve class com.my.import.four.Import4DTO;
unable to resolve class com.my.import.five.Import5DTO;
Run Code Online (Sandbox Code Playgroud)
在解析基类之前,我是否可以获得我期望的行为而不解析每个导入类?
谢谢!
由GroovyShell / Groovy脚本引起的内存泄漏(请参阅最后的GroovyEvaluator代码)。主要问题是(从MAT分析器复制粘贴):
由“ <系统类加载器>”加载的类“ java.beans.ThreadGroupContext”占用807,406,960(33.38%)个字节。
和:
由“ sun.misc.Launcher $ AppClassLoader @ 0x7004e9c80”加载的“ org.codehaus.groovy.reflection.ClassInfo $ ClassInfoSet $ Segment”的16个实例占用1,510,256,544(62.44%)字节
我们正在使用Groovy 2.3.11和Java8(确切地说是1.8.0_25)。
升级到Groovy 2.4.6不能解决问题。只是提高了内存使用一个 小 一点,ESP。非堆。
我们正在使用的Java参数:-XX:+ CMSClassUnloadingEnabled -XX:+ UseConcMarkSweepGC
顺便说一句,我已阅读https://dzone.com/articles/groovyshell-and-memory-leaks。当不再需要GroovyShell shell时,我们会将其设置为null。使用GroovyShell()。parse()可能会有所帮助,但这对我们来说不是一个选择-我们有10多个集合,每个集合由20-100个脚本组成,并且可以随时更改(在运行时)。
设置MaxMetaspaceSize也应该有所帮助,但这并不能真正解决根本问题,也不能消除根本原因。所以我仍在努力确定。
我创建了负载测试来重新创建问题(请参阅最后的代码)。当我运行它时:
正如我已经提到的,我正在使用MAT分析堆转储。因此,让我们检查Dominator树报告:
Hashmap占用了30%以上的堆。因此,让我们进一步分析它。让我们看看里面有什么。让我们检查哈希条目:
报告了38 830个条目。包括38780个条目,这些条目的键匹配“ .class Script。 ”。
另一件事,“重复的类”报告:
我们有400个条目(因为负载测试定义了400个G.script),所有这些条目都属于“ ScriptN”类。它们都持有对groovyclassloader $ innerloader的引用
我发现了类似的错误报告:https : //issues.apache.org/jira/browse/GROOVY-7498(请参阅最后的评论和屏幕截图)-通过将Java升级到1.8u51解决了他们的问题。但是,这对我们并没有招数。
我们的代码:
public class GroovyEvaluator
{
private GroovyShell shell;
public GroovyEvaluator()
{ …Run Code Online (Sandbox Code Playgroud) 我正在尝试在隔离的类加载器中运行groovy脚本,以便它们不会在调用类的依赖项的上下文中执行.
Path log4j = Paths.get("..../lib/log4j-1.2.17.jar");
Path groovy = Paths.get("..../lib/groovy-all-2.1.3.jar");
RootLoader rootLoader = new RootLoader(new URL[] { log4j.toUri().toURL(), groovy.toUri().toURL() }, null);
GroovyScriptEngine engine = new GroovyScriptEngine(".../src/main/resources", rootLoader);
engine.run("Standalone.groovy", "");
Run Code Online (Sandbox Code Playgroud)
Standalone.groovy:
import org.apache.log4j.BasicConfigurator
import org.apache.log4j.Logger
Logger logger = Logger.getLogger(getClass())
BasicConfigurator.configure()
logger.info("hello world")
Run Code Online (Sandbox Code Playgroud)
pom.xml摘录:
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.1.3</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)
我试过的上述任何变化都会导致
Exception in thread "main" groovy.lang.GroovyRuntimeException: Failed to create Script instance for class: class Standalone. Reason: java.lang.ClassCastException: Standalone cannot be cast to groovy.lang.GroovyObject
at org.codehaus.groovy.runtime.InvokerHelper.createScript(InvokerHelper.java:443)
at groovy.util.GroovyScriptEngine.createScript(GroovyScriptEngine.java:564)
at groovy.util.GroovyScriptEngine.run(GroovyScriptEngine.java:551)
at groovy.util.GroovyScriptEngine.run(GroovyScriptEngine.java:537) …Run Code Online (Sandbox Code Playgroud) 假设我使用Groovyc编译了一个Groovy脚本,它在文件系统中生成了一个或多个.class文件.从Java应用程序中,如何动态地将这些类添加到类路径中以加载它们并调用它们的方法?目标是预编译Groovy脚本并将它们存储到数据库中,因此可以从脚本的编译版本执行评估.
我尝试从Java类调用我自己的groovy脚本函数,用户也可以使用标准表达式.
例如:
GroovyShell shell = new GroovyShell();
Script scrpt = shell.parse("C:/Users/Cagri/Desktop/MyCustomScript.groovy");
Binding binding = new Binding();
binding.setVariable("str1", "foo");
binding.setVariable("str2", "boo");
scrpt.setBinding(binding);
System.out.println(scrpt.evaluate("customConcat(str1, str2)")); //my custom method
System.out.println(scrpt.evaluate("str1.concat(str2)"));
Run Code Online (Sandbox Code Playgroud)
这是MyCustomScript.groovy
def area(def sf) {
Feature f = new Feature(sf);
f.getGeom().area;
}
def customConcat(def string1, def string2) {
string1.concat(string2)
}
Run Code Online (Sandbox Code Playgroud)
运行时,此行scrpt.evaluate("str1.concat(str2)")按预期工作,但scrpt.evaluate("customConcat(str1, str2)")会引发异常:
groovy.lang.MissingMethodException: No signature of method: Script1.customConcat() is applicable for argument types: (java.lang.String, java.lang.String) values: [foo, boo]
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:55)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:78)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:49)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:133)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:145)
at …Run Code Online (Sandbox Code Playgroud) 我试图建立一个类似于的动态查询:
def domain = DomainName
def ids = 1
def domainClass = "$domain" as Class
domainClass.find("from ${domain} as m where m.job = ${ids} ").id
Run Code Online (Sandbox Code Playgroud)
但它不起作用.
如果我正在尝试这个,一切都很好:
def domain = DomainName
def ids = 1
DomainName.find("from ${domain} as m where m.job = ${ids} ").id
Run Code Online (Sandbox Code Playgroud)
如何在find中使用动态域名类?
我使用 GroovyClassLoader.parseClass(src) “即时”编译单个 groovy 源模块,一切正常。
但问题是当这个源模块导入其他类时,这些类还没有编译。当我开始编译一个源代码但其他源代码是必需的并且在源路径上准备就绪时,传统编译也会进行编译。
如何使用带有目标的 GroovyClassLoader 来编译所有其他所需的源而不是来自 FILESYSYSTEM。我的来源例如在数据库中、通过 URI 的远程 http 等。
groovy ×8
java ×4
classloader ×3
groovyshell ×3
classpath ×1
gradle ×1
grails ×1
grails-orm ×1
java-7 ×1
java-8 ×1
memory-leaks ×1
scriptengine ×1