简体   繁体   中英

How can I access injected Grails beans in an abstract class method?

I have an abstract class that implements much of the functionality inherited by a large number of concrete classes that are registered as beans. The beans are defined with autowiring on. For example:

abstract class MyAbstract {

    MyService myService
    MyBean myBean

    def doSomething() {
        def value = myService.something(myBean)
    }
}


class MyConcrete extends MyAbstract {
    def concreteField

    def doSomethingElse() {
         def value = myService.somethingElse(myBean)
    }
}

conf/spring/resources.groovy:

myConcrete(MyConcrete) { bean ->
    bean.autowire = true
    myBean = ref(MySpecificBeanImpl)
}

My Problem:

When I run the method doSomethingElse in a MyConcrete instance, everything works as expected and the myService and myBean values are filled in with the proper values by DI. When I execute the doSomething method in a MyConcrete instance, both the myService and myBean values are null. It appears that the DI values are not visible in the abstract method inherited by the subclass. That really sucks.

I can manually access the values using a context holder in the method or I can pass the values from the subclass to the abstract parent class using a modified method signature that accepts those values as parameters, but these are no good solutions. It completely breaks the usefulness of abstract class implementations and requires a lot of replicated code that I don't want to have to maintain.

Even worse, in my specific case, the value of myBean is actually different for each concrete class, explicitly wired in the resources.groovy file, so the generic holder approach doesn't work.

I've looked through a number of posts relating to this including Grails services in abstract class without much result in figuring out what is going on. The abstract bean definition seems to be about abstracting the bean definition properties and doesn't have anything to do with abstract classes and subclass inheritance.

(1) Is this a limitation in the Grails/Spring DI support? (2) Is there something else I need to do wrt the abstract class?

You have left out some details and I have had to make some assumptions but I have created an app with something similar to what you are describing. The project at https://github.com/jeffbrown/abstractbeanmethods contains the following and appears to work:

src/groovy/demo/MyAbstract.groovy package demo

abstract class MyAbstract {

    MyService myService
    MyBean myBean

    def doSomething() {
        myService.something(myBean)
    }
}

src/groovy/demo/MyConcrete.groovy

package demo

class MyConcrete extends MyAbstract {
    def doSomethingElse() {
         def value = myService.somethingElse(myBean)
    }
}

grails-app/conf/spring/resources.groovy

// Place your Spring DSL code here
beans = {
    myBeanImpl demo.MySpecificBeanImpl

    myConcrete(demo.MyConcrete) { bean ->
        bean.autowire = true
        myBean = ref('myBeanImpl')
    }
}

src/groovy/demo/MySpecificBeanImpl.groovy package demo

class MySpecificBeanImpl implements MyBean {
}

src/groovy/demo/MyBean.groovy

package demo

interface MyBean {}

grails-app/service/demo/MyService.groovy package demo

class MyService {

    def something(MyBean bean) {
        "Bean class name is ${bean.class.name} in MyService.something() method"
    }
    def somethingElse(MyBean bean) {
        "Bean class name is ${bean.class.name} in MyService.somethingElse() method"
    }
}

grails-app/controllers/demo/DemoController.groovy package demo

class DemoController {
    def myConcrete
    def index() {
        def sb = new StringBuffer()
        sb << myConcrete.doSomething()
        sb << " and "
        sb << myConcrete.doSomethingElse()
        render sb
    }
}

You may find some significant difference between something there and someting that you are doing. If you can isolate a problematic scenario in that sample app, or provide a runnable version of your code then I will be happy to straighten it out for you.

I solved this problem using @Autowired annotation

import grails.gorm.services.Service
import org.springframework.beans.factory.annotation.Autowired

@Service(User)
abstract class UserService {

    @Autowired
    OrganizationService organizationService

    //...
}

Tested on Grails 3.3.5

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM