Question

I know that I can replace a Spring bean provided by Grails simply by defining my own bean with the same name. For example, if I want to replace the messageSource bean provided by Grails

class MyMessageSource implements MessageSource {
    // methods omitted
}

Then add the following in resources.groovy

messageSource(MyMessageSource)

However, assume that I want MyMessageSource to decorate the implementation of this bean provided by Grails

class MyMessageSource implements MessageSource {

    // this field should be set to the MessageSource impl provided by Grails
    MessageSource messageSource
}

I can't figure out how to wire this up in resources.groovy. Obviously I can't do this:

messageSource(MyMessageSource) {
    messageSource = ref('messageSource')
}

Because it looks like I'm defining a bean that depends on itself. I could of course give my bean a different name, e.g.

myMessageSource(MyMessageSource) {
    messageSource = ref('messageSource')
}

But then any class beyond my control (e.g. a plugin class) that declares a dependency on messageSource will be set to the bean provided by Grails rather than my decorator.

Was it helpful?

Solution

In an application (not a plugin), something along these lines should work...

The new message source:

// src/groovy/com/demo/MyMessageSource.groovy
package com.demo

import org.springframework.context.MessageSource
import org.springframework.context.MessageSourceResolvable
import org.springframework.context.NoSuchMessageException

class MyMessageSource implements MessageSource {
    MessageSource theRealMessageSource

    String getMessage(String code, Object[] args, String defaultMessage, Locale locale) {
        theRealMessageSource.getMessage code, args, defaultMessage, locale
    }

    String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException {
        theRealMessageSource.getMessage code, args, locale
    }

    String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException {
        theRealMessageSource.getMessage resolvable, locale
    }
}

A post processor:

// src/groovy/com/demo/MyPostProcessor.groovy
package com.demo

import org.springframework.beans.factory.config.BeanFactoryPostProcessor
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory
import org.springframework.beans.BeansException

class MyPostProcessor implements BeanFactoryPostProcessor {

    @Override
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // error handling ommitted for brevity here...

        def realMessageSource = beanFactory.getBean('messageSource')
        def newMessageSource = new MyMessageSource()
        newMessageSource.theRealMessageSource = realMessageSource

        beanFactory.removeBeanDefinition 'messageSource'
        beanFactory.registerSingleton 'messageSource', newMessageSource
    }
}

Register the post processor:

// grails-app/conf/spring/resources.groovy
beans = {
    myMessageSourcePostProcessor com.demo.MyPostProcessor
}

OTHER TIPS

After the context is initialized you can retrieve the messageSource bean, create an instance of your bean that delegates to the messageSource bean and then register your bean with the context under the name "messageSource" so when DI happens, your bean is the bean that will be used. In a plugin you should be able to do this in doWithApplicationContext (not doWithSpring).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top