Grails 2.4.2 bean spring bean 注入

Grails 2.4.2 bean spring bean injection

示例应用位于此处:https://github.com/rushidesai1/Grails2_4_2_BeanIssue

问题:

在resources.groovy中如果我们像这样声明一个bean

beans = {
    testObject(TestObject){bean ->
        bean.scope = "prototype"
        map = new HashMap()  // or [:]
        //And also if we declare any object like this
        testA = new TestA()
  }
}

现在如果我们 DI testObject bean 或做 'Holders.grailsApplication.mainContext.getBean("testObject")',那么我们得到的 bean 将有单例 'map' 和单例 'testA' 对象。

这里 testObject 被声明为 'prototype' 即使这样 'map' 和 'testA' 都是单例

我想知道这是一个错误还是它按设计工作。它会像这样工作是完全违反直觉的,因为我们专门做新的,所以我们希望每次都注入一个新的 bean。

使用单元测试用例查看我的问题的更详细版本。

提前感谢您的澄清!!!

I want to know if this is a bug or it is working as designed.

是的,我认为它按设计工作。

您的 testObject bean 是单例。该单例 bean 只有 maptestA 属性的 1 个副本。您所描述的行为正是我所期望的。

编辑:

我已经审查了链接项目中的应用程序,这是正在发生的事情...

resources.groovy 你有这样的东西:

testObject(TestObject) { bean -> bean.scope = "prototype" mapIssue1 = ["key1FromResource.groovy": "value1FromResource.groovy"] }

那个 testObject bean 是一个原型范围的 bean,所以每次检索一个时,您都会得到一个新实例。但是,您在 bean 定义中硬编码了初始化 Map,因此创建的 bean 定义具有与之关联的 Map,因此从该 bean def 创建的每个 bean 都将具有相同的 Map .如果你想要一个不同的 Map 实例,你可以在 afterPropertiesSet 或类似的中创建它。

https://github.com/rushidesai1/Grails2_4_2_BeanIssue/blob/e9b7c9e70da5863f0716b998462eca60924ee717/test/unit/test/SpringBeansSpec.groovy处的单元测试写得不是很好。查看发生了什么依赖于在所有这些 printlns 之后询问 stdout。这种行为可以用这样的东西更简单地验证:

资源:groovy

import test.TestObject

beans = {
    testObject(TestObject) { bean ->
        bean.scope = "prototype"
        mapIssue1 = ["key1FromResource.groovy":"value1FromResource.groovy"]
    }
}

SpringBeansSpec.groovy

package test

import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import spock.lang.Specification

@TestMixin(GrailsUnitTestMixin)
class SpringBeansSpec extends Specification {
    static loadExternalBeans = true 

    void 'test bean properties'() {
        setup:
        def testObject1 = grailsApplication.mainContext.testObject
        def testObject2 = grailsApplication.mainContext.testObject

        expect: 'test TestObject beans are not the same instance'
        !testObject1.is(testObject2)

        and: 'the TestObject beans share values defined in the bean definition'
        testObject1.mapIssue1.is(testObject2.mapIssue1)
    }
}

一方面,即使您使用的是 new ,它也应该在每次获取 testA bean 时创建一个新对象,另一方面,它会按预期工作,这可能会让人感到困惑。怎么样?

好的!所以答案在于Spring java配置。 resources.groovy 正在使用 DSL,它在内部是一个配置文件。

不确定您是否知道或记得 springs @Configuration 注释。使用它,我们将 POJO 设为配置文件。 现在Spring的规则是:

  1. 除非指定,否则默认情况下创建的任何 bean 都是单例的。
  2. 即使您在 java 配置文件中使用 new。 Spring 是一个 spring 配置文件,因此 new 并不总是意味着一个新对象。

因此,对于等效的配置文件,如果我跳过 testObject 并且现在映射如下:

@Configuration
public class JavaConfiguration {

    @Bean
    public Teacher teacher(){
        TestA testA =  new TestA();
        return teacher;
    }
}

在这里,我们使用了new TestA()。但是 spring 将始终 return 相同的对象,直到您明确指定使用范围原型。 因此,在启用原型范围后,上面的配置文件将如下所示:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class JavaConfiguration {

    @Bean
    @Scope(value="prototype")
    public Teacher teacher(){
        TestA testA =  new TestA();
        return teacher;
    }
}

对应的 DSL 为:

testA(TestA){bean->
 bean.scope='prototype'
}

希望对您有所帮助!!