springgrailsgrails-2.4spring-dslspring-groovy-config

Grails 2.4.2 bean spring bean injection


Sample app located here : https://github.com/rushidesai1/Grails2_4_2_BeanIssue

Question:

In resources.groovy if we declare a bean like this

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

and Now if we DI testObject bean or do 'Holders.grailsApplication.mainContext.getBean("testObject")', then the bean we get will have singleton 'map' and singelton 'testA' object.

Here testObject is declared as 'prototype' and even then both 'map' and 'testA' are singleton

I want to know if this is a bug or it is working as designed. It is completely counter intuitive that it would work like this since we are specifically doing new and so we expect a new bean being injected everytime.

Use the Unit test case to see more detailed version of my question.

Thanks in advance for clarification !!!


Solution

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

    Yes, I think it is working as designed.

    Your testObject bean is a singleton. That singleton bean only has 1 copy of the map and testA properties. The behavior you are describing is exactly what I would expect.

    EDIT:

    I have reviewed the application in the linked project and this is what is going on...

    In resources.groovy you have something like this:

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

    That testObject bean is a prototype scoped bean so each time you retrieve one, you will get a new instance. However, you have the initialization Map hardcoded in the bean definition so the bean definition that is created has that Map associated with it so every bean created from that bean def will have the same Map. If you want a different Map instance, you could create it in afterPropertiesSet or similar.

    The unit test at https://github.com/rushidesai1/Grails2_4_2_BeanIssue/blob/e9b7c9e70da5863f0716b998462eca60924ee717/test/unit/test/SpringBeansSpec.groovy is not very well written. Seeing what is going on relies on interrogating stdout after all of those printlns. The behavior could be more simply verified with something like this:

    resources: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)
        }
    }