我有一个可以运行脚本来自动执行某些任务的应用程序。我想在这些脚本中使用元编程来优化代码大小和可读性。所以而不是:
try {
def res = factory.allocate();
... do something with res ...
} finally {
res.close()
}
我想
Factory.metaClass.withResource = { c ->
try {
def res = factory.allocate();
c(res)
} finally {
res.close()
}
}
所以脚本编写者可以写:
factory.withResource { res ->
... do something with res ...
}
(我可以做正确的错误处理等)。
现在我想知道何时以及如何实现这一点。我可以在我添加到每个脚本的标题中附加元类的操作但是我担心如果两个用户同时运行脚本会发生什么(并发访问元类)。
元类的范围是什么?编译器?脚本环境? Java VM?加载Groovy的类加载器?
我的理由是,如果Groovy元类具有VM范围,那么我可以在启动期间运行一次安装脚本。
答案 0 :(得分:3)
每个类加载器存在元类 [citation needed] :
档案/tmp/Qwop.groovy
:
class Qwop { }
档案/tmp/Loader.groovy
:
Qwop.metaClass.bar = { }
qwop1 = new Qwop()
assert qwop1.respondsTo('bar')
loader = new GroovyClassLoader()
clazz = loader.parseClass new File("/tmp/Qwop.groovy")
clazz.metaClass.brap = { 'oh my' }
qwop = clazz.newInstance()
assert !qwop.respondsTo('bar')
assert qwop1.respondsTo('bar')
assert qwop.brap() == "oh my"
assert !qwop1.respondsTo('brap')
// here be custom classloaders
new GroovyShell(loader).evaluate new File('/tmp/QwopTest.groovy')
用于测试范围元类(/tmp/QwopTest.groovy
)的脚本:
assert !new Qwop().respondsTo('bar')
assert new Qwop().respondsTo('brap')
执行:
$ groovy Loaders.groovy
$
如果您有一组定义良好的类,则可以根据添加的brap
方法在类加载器加载的类之上应用元编程。
答案 1 :(得分:2)
对于很多场景来说,这类事情的另一个选择是使用扩展模块。
package demo
class FactoryExtension {
static withResource(Factory instance, Closure c) {
def res
try {
res = instance.allocate()
c(res)
} finally {
res?.close()
}
}
}
将其编译并放入一个包含META-INF/services/org.codehaus.groovy.runtime.ExtensionModule
文件的jar文件中,该文件包含类似的内容......
moduleName=factory-extension-module
moduleVersion=1.0
extensionClasses=demo.FactoryExtension
然后,为了让某人使用您的扩展,他们只需要将该jar文件放在他们的CLASSPATH上。有了所有这些,用户可以做这样的事情......
factoryInstance.withResource { res ->
... do something with res ...
}
有关扩展模块的更多信息,请访问http://docs.groovy-lang.org/docs/groovy-2.3.6/html/documentation/#_extension_modules。