即使使用DELEGATE_FIRST / DELEGATE_ONLY解析策略

时间:2016-03-29 14:14:23

标签: grails groovy grails-2.0

我有一个扩展所有域类的方法:

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>

我在这里做错了什么?

2 个答案:

答案 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