Scope of Groovy's metaClass?

老子叫甜甜 提交于 2019-12-12 16:03:29

问题


I have an application which can run scripts to automate certain tasks. I'd like to use meta programming in these scripts to optimize code size and readability. So instead of:

try {
    def res = factory.allocate();
    ... do something with res ...
} finally {
    res.close()
}

I'd like to

Factory.metaClass.withResource = { c -> 
    try {
        def res = factory.allocate();
        c(res)
    } finally {
        res.close()
    }
}

so the script writers can write:

factory.withResource { res ->
    ... do something with res ...
}

(and I could do proper error handling, etc).

Now I wonder when and how I can/should implement this. I could attach the manipulation of the meta class in a header which I prepend to every script but I'm worried what would happen if two users ran the script at the same time (concurrent access to the meta class).

What is the scope of the meta class? The compiler? The script environment? The Java VM? The classloader which loaded Groovy?

My reasoning is that if Groovy meta classes have VM scope, then I could run a setup script once during startup.


回答1:


Metaclasses exist per classloader [citation needed]:

File /tmp/Qwop.groovy:

class Qwop { }

File /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')

And a script to test the scoped metaclass (/tmp/QwopTest.groovy):

assert !new Qwop().respondsTo('bar')
assert new Qwop().respondsTo('brap')

Execution:

$ groovy Loaders.groovy 
$ 

If you have a set of well defined classes you could apply metaprogramming on top of the classes loaded by your classloader, as per the brap method added.




回答2:


Another option for this sort of thing which is better for a lot of scenarios is to use an extension module.

package demo

class FactoryExtension {
    static withResource(Factory instance, Closure c) {
        def res
        try {
            res = instance.allocate()
            c(res)
        } finally {
            res?.close()
        }
    }
}

Compile that and put it in a jar file which contains a file at META-INF/services/org.codehaus.groovy.runtime.ExtensionModule that contains something like this...

moduleName=factory-extension-module
moduleVersion=1.0
extensionClasses=demo.FactoryExtension

Then in order for someone to use your extension they just need to put that jar file on their CLASSPATH. With all of that in place, a user could do something like this...

factoryInstance.withResource { res ->
    ... do something with res ...
}

More information on extension modules is available at http://docs.groovy-lang.org/docs/groovy-2.3.6/html/documentation/#_extension_modules.



来源:https://stackoverflow.com/questions/25974525/scope-of-groovys-metaclass

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!