2

示例应用程序位于此处: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 被声明为“原型”,即使这样,“地图”和“测试A”都是单例的

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

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

提前感谢您的澄清!

4

2 回答 2

2

我想知道这是一个错误还是按设计工作。

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

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

编辑:

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

resources.groovy你有这样的事情:

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

testObjectbean 是一个原型作用域的 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的单元测试写得不是很好。看到发生了什么依赖于在所有这些 println 之后询问标准输出。可以通过以下方式更简单地验证行为:

资源: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)
    }
}
于 2016-08-25T06:52:45.243 回答
0

一方面,即使您使用 new 它也应该在每次获取 testA bean 时创建一个新对象,而另一方面它按预期工作,这可能会令人困惑。如何?

好吧!所以答案就在 Spring java Configuration 中。resources.groovy正在使用内部是配置文件的 DSL 。

不确定您是否知道或记得弹簧 @Configuration 注释。使用它我们正在使 POJO 成为一个配置文件。现在 Spring 的规则是:

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

因此,对于等效的配置文件,如果我现在跳过 testObject 和 map 如下:

@Configuration
public class JavaConfiguration {

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

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

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'
}

希望能帮助到你!!

于 2016-08-25T13:26:04.143 回答