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 只有 map
和 testA
属性的 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的规则是:
- 除非指定,否则默认情况下创建的任何 bean 都是单例的。
- 即使您在 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'
}
希望对您有所帮助!!
示例应用位于此处: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 只有 map
和 testA
属性的 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的规则是:
- 除非指定,否则默认情况下创建的任何 bean 都是单例的。
- 即使您在 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'
}
希望对您有所帮助!!