【问题标题】:Exception accessing Spring managed bean from Groovy closure从 Groovy 闭包访问 Spring 托管 bean 的异常
【发布时间】:2015-07-07 08:30:41
【问题描述】:

我有一个简单的 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 {} 闭包给出了一个例外。有什么想法吗?

【问题讨论】:

  • 什么版本的 Groovy?
  • 这只是 ClosuresApplication 的银行成员是私有的问题,因此没有创建 setter/getter,自动装配无法设置值吗?
  • 很奇怪。我建议作为针对 Groovy 的错误提交。

标签: spring groovy spring-boot


【解决方案1】:

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

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

这似乎是一个奇怪的范围界定问题,我一直无法确定其真正原因。

【讨论】:

    【解决方案2】:

    另一种可能的解决方案是通过删除“私人”修饰符将bank 字段转换为常规属性。

     @Autowired
     Bank bank
    

    这在视觉上稍微不那么可怕(尤其是当您遇到多个字段的问题时),但它确实改变了该字段的访问级别。

    我遇到过很多这样的问题,似乎都与在 spring 管理的 bean 中的各种闭包中使用私有字段有关,特别是当 bean 被 spring (EnhancerBySpringCGLIB)“增强”(代理)时。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-27
      • 1970-01-01
      • 2012-11-08
      • 2012-03-24
      • 2016-04-16
      相关资源
      最近更新 更多