Spring Bean Aliases in Grails
Tuesday, January 06th, 2009I was looking at the source code of a Spring application recently and saw an applicationContext.xml
element that I hadn’t seen before: <alias>. It turns out this has been around for quite a while, since v1.2 (2005). It sure would have been convenient several months ago.
My first Grails app was a Live Chat app for tech support. I used Comet, so I needed to access Grails beans from Java classes. I did this by having my Services implement an interface, and used that as the type for the Spring bean dependency injection.
But the problem with that was that in Grails you don’t get to choose the bean names of artifacts – there’s a strict convention, so a Service named ChatService
is registered as chatService
. But the convention I’ve always used is to name my interface ChatService
and name the implementation class ChatServiceImpl
. But then the Spring bean name would be chatServiceImpl
.
In a regular Spring app I’d implicitly alias when I added the bean element to the applicationContext.xml
file, e.g.
<bean id='chatService' class='com.foo.bar.ChatServiceImpl' />
So instead I chose an option that I really dislike – I named the interface IChatService
and the Grails Service ChatService
.
But with the <alias> tag, I should be able to do what I want – name the interface ChatService
and the Grails Service ChatServiceImpl
, and add
<alias name='chatServiceImpl' alias='chatService' />
to resources.xml
. The original bean name will still be there, but I can use the more natural bean name for dependency injection.
This doesn’t cause an error, but it doesn’t work either. I spent some time in a debugger and the alias is registered, but the application context that’s available when the app is running is different from the one that the alias was added to.
But it turns out that you can add programatically add aliases at any time, so I added the alias call to BootStrap.groovy
:
class BootStrap { def grailsApplication def init = { servletContext -> grailsApplication.mainContext.registerAlias('chatServiceImpl', 'chatService') } def destroy = {} }
and now I can use the chatService
alias as the bean name.
I noticed though that since aliases are stored in a Map
and not directly in the bean registry, that ctx.getBeanDefinitionNames()
only returns ‘real’ bean names and no aliases. But it turns out that it’s pretty simple to get all bean names and aliases:
def allNames = [] as Set ctx.beanDefinitionNames.each { name -> allNames << name ctx.getAliases(name).each { alias -> allNames << alias } }