从Groovy闭包访问Spring托管bean的异常

时间:2015-04-28 13:00:52

标签: spring groovy spring-boot

我有一个简单的Spring Boot应用程序,它有2个bean类,一个主类和一个配置类。每当我尝试从Groovy闭包中访问Spring托管的Bank bean时,我都会遇到异常:

Exception in thread "main" java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:304)
    at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:675)
    at com.example.closures.ClosuresApplication$_run_closure1.doCall(ClosuresApplication.groovy:22)
    at org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:690)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:321)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:957)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:946)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.springframework.boot.SpringApplication$run.call(Unknown Source)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:324)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:292)
    at com.example.closures.ClosuresApplication.main(ClosuresApplication.groovy:27)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1016)
Caused by: groovy.lang.MissingPropertyException: No such property: bank for class: com.example.closures.ClosuresApplication$$EnhancerBySpringCGLIB$$44735576
    at groovy.lang.Closure.call(Closure.java:423)
    at groovy.lang.Closure.call(Closure.java:439)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2027)
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:51)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2012)
    at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:49)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2053)
    at org.codehaus.groovy.runtime.dgm$162.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:304)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:271)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:53)
    at com.example.closures.ClosuresApplication$_run_closure1.doCall(ClosuresApplication.groovy:22)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:110)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:122)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.example.closures.ClosuresApplication.run(ClosuresApplication.groovy:21)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:90)
    at org.springframework.boot.SpringApplication.runCommandLineRunners(SpringApplication.java:672)
    ... 9 common frames omitted

Bank.groovy

@Component
final class Bank {
    final String name = 'MyBank'
    final AutomatedTellerMachine insideAtm
    final AutomatedTellerMachine outsideAtm

    @Autowired
    Bank(@Qualifier('insideAtm') final AutomatedTellerMachine insideAtm, @Qualifier('outsideAtm') final AutomatedTellerMachine outsideAtm) {
        this.insideAtm = insideAtm
        this.outsideAtm = outsideAtm
    }

    String getName() {
        return name
    }

    AutomatedTellerMachine getInsideAtm() {
        return insideAtm
    }

    AutomatedTellerMachine getOutsideAtm() {
        return outsideAtm
    }
}

AutomatedTellerMachine.groovy

final class AutomatedTellerMachine {
    final String name

    AutomatedTellerMachine(final String name) {
        this.name = name
    }

    String getName() {
        return name
    }
}

AppConfig.groovy

@Configuration
class AppConfig {

    @Bean(name = 'insideAtm')
    AutomatedTellerMachine getInsideAtm() {
        new AutomatedTellerMachine('insideAtm')
    }

    @Bean(name = 'outsideAtm')
    AutomatedTellerMachine getOutsideAtm() {
        new AutomatedTellerMachine('outsideAtm')
    }
}

ClosuresApplication.groovy

@SpringBootApplication
class ClosuresApplication implements CommandLineRunner {

    @Autowired
    private Bank bank

    @Override
    void run(String... args) throws Exception {
        for (def i = 0; i < 10; i++) {
            printf 'Bank %02d: %s%n', (i + 1), bank
        }

        (1..10).each {
            printf 'Bank %02d: %s%n', it, bank
        }
    }

    static void main(String[] args) {
        SpringApplication.run ClosuresApplication, args
    }
}

常规for循环工作正常,但Groovy的.each {}闭包给出了异常。有什么想法吗?

2 个答案:

答案 0 :(得分:2)

我在很多时候遇到过这个问题并发现一个简单的解决方法可以修复它 - 在bank方法中添加对run变量的引用:

def _bank = bank
(1..10).each {
    printf 'Bank %02d: %s%n', it, _bank
}

这似乎是一个奇怪的范围问题,我从来没有能够确定真正的原因。

答案 1 :(得分:2)

另一种可能的解决方案是将bank字段转换为groovy属性,方法是删除“私有”字样。修改。

 @Autowired
 Bank bank

这在视觉上稍微有点可怕(特别是如果您遇到多个字段的此问题),但确实会改变该字段的访问级别。

我遇到了很多这些问题,所有这些问题似乎都与在spring-managed bean中的各种闭包中使用私有字段有关,特别是当bean被增强时#39; (代理)由spring(EnhancerBySpringCGLIB)。