我有一个扩展所有域类的方法:
static def bindGormStaticApiExtensions() {
Holders.grailsApplication.domainClasses.each { domainClass ->
domainClass.metaClass.static.withDataSource = { DataSource ds, Closure callable ->
HibernateDatastore datastore = Holders.applicationContext.getBean(HibernateDatastore)
SessionFactory sessionFactory = datastore.sessionFactory
Session session = null
Connection connection = null
try {
SessionBuilder sb = sessionFactory.withOptions()
connection = ds.getConnection()
session = sb.connection(connection).openSession()
callable.delegate = delegate
callable.resolveStrategy = Closure.DELEGATE_FIRST
return callable?.call(session)
} catch (Exception e) {
LOG.error("An error occured", e)
} finally {
session?.close()
if(connection && !connection.closed) {
connection.close()
}
}
}
}
}
然而,当我在域类上调用此方法时,我必须使用delegate.findByXXX()
否则groovy使用owner
,即使我已将闭包解析策略明确设置为DELEGATE_FIRST
。 / p>
我在这里做错了什么?
答案 0 :(得分:1)
你是对的,问题出在Groovy中。这是一个简单的测试,可以证明这个问题:
assert MyClass.thisMethodDoesNotExist() == 'You called static method thisMethodDoesNotExist'
assert new MyClass().thisMethodDoesNotExist() == 'You called instance method thisMethodDoesNotExist'
new MyClass().with {
assert thisMethodDoesNotExistEither() == 'You called instance method thisMethodDoesNotExistEither'
}
MyClass.with {
// The following method call will throw a groovy.lang.MissingMethodException
assert thisMethodDoesNotExistEither() == 'You called static method thisMethodDoesNotExistEither'
}
class MyClass {
static Object $static_methodMissing(String name, Object args) {
"You called static method $name"
}
Object methodMissing(String name, Object args) {
"You called instance method $name"
}
}
stackstrace看起来像这样:
groovy.lang.MissingMethodException: No signature of method: ConsoleScript10.thisMethodDoesNotExistEither() is applicable for argument types: () values: []
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:81)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:158)
at ConsoleScript10$_run_closure2.doCall(ConsoleScript10:10)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
at groovy.lang.Closure.call(Closure.java:426)
at groovy.lang.Closure.call(Closure.java:442)
at org.codehaus.groovy.runtime.DefaultGroovyMethods.with(DefaultGroovyMethods.java:241)
at org.codehaus.groovy.runtime.dgm$757.invoke(Unknown Source)
at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite$StaticMetaMethodSiteNoUnwrapNoCoerce.invoke(StaticMetaMethodSite.java:151)
at org.codehaus.groovy.runtime.callsite.StaticMetaMethodSite.call(StaticMetaMethodSite.java:91)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
at ConsoleScript10.run(ConsoleScript10:8)
at groovy.lang.GroovyShell.runScriptOrMainOrTestOrRunnable(GroovyShell.java:263)
at groovy.lang.GroovyShell.run(GroovyShell.java:524)
at groovy.lang.GroovyShell.run(GroovyShell.java:503)
at groovy.lang.GroovyShell.run(GroovyShell.java:170)
at groovy.lang.GroovyShell$run$1.call(Unknown Source)
at groovy.ui.Console$_runScriptImpl_closure17.doCall(Console.groovy:980)
at groovy.ui.Console$_runScriptImpl_closure17.doCall(Console.groovy)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1019)
at groovy.lang.Closure.call(Closure.java:426)
at groovy.lang.Closure.call(Closure.java:420)
at groovy.lang.Closure.run(Closure.java:507)
at java.lang.Thread.run(Thread.java:724)
我怀疑,我不完全了解Groovy的MOP实施,所以我不知道如何解决这个问题。有一个开放的bug反映了这个问题。
static def bindGormStaticApiExtensions() {
Holders.grailsApplication.domainClasses.each { domainClass ->
domainClass.metaClass.static.withDataSource = { DataSource ds, Closure callable ->
HibernateDatastore datastore = Holders.applicationContext.getBean(HibernateDatastore)
SessionFactory sessionFactory = datastore.sessionFactory
Session session = null
Connection connection = null
try {
SessionBuilder sb = sessionFactory.withOptions()
connection = ds.getConnection()
session = sb.connection(connection).openSession()
// Use a proxy as the delegate instead of the domain class.
callable.delegate = new ClassProxy(delegate)
callable?.call(session)
} catch (Exception e) {
LOG.error("An error occured", e)
} finally {
session?.close()
if(connection && !connection.closed) {
connection.close()
}
}
}
}
}
这是代理:
// src/main/groovy/some/package/ClassProxy.groovy
@groovy.transform.TupleConstructor
/*
* Create an instance of my like so: new ClassProxy(SomeClass)
* and I will delegate method calls to the Class,
* essentially converting instance method calls to Class static
* method calls.
*/
class ClassProxy {
Class clazz
Object methodMissing(String name, Object args) {
clazz.invokeMethod(name, args)
}
}
methodMissing()
,这是动态查找器所依赖的,适用于实例,因此代理利用这一点,只需在真实类上调用您调用它的任何方法。在这种情况下是一个域类。我不确定您是否需要更改默认的解决策略,但我不这么认为。在我的测试中,这是不必要的。
答案 1 :(得分:0)
...否则groovy使用所有者,即使我已经明确设置了 关闭将策略解析为DELEGATE_FIRST。
我认为这不正确。
您有以下内容:
callable.delegate = delegate
这是您要分配给withDataSource
的闭包内部。
当你以这种方式使用ExpandoMetaClass
时,闭包的委托将是你调用该方法的东西。在您的情况下,您将调用withDataSource
。