Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Autowiring custom-made Spring component in Grails

I have a custom-made Spring which is bundled in a jar, which is then set as a dependency to my Grails app. I load the app-context for the bean with importBeans statement in resoueces.groovy like

beans = {
  importBeans('classpath:app-context-my-component.xml')
}

The app-context-my-component.xml has some bean definitions and the following lines

<context:annotation-config />
<context:property-placeholder location="classpath:my-component.properties" />

The component I'm trying to use with Grails is annotated with @Component("myComponent").

Grails is loading the external application context. I know it because I first didn't have the .properties file on classpath and there we're no fallback mechanism for missing property in my @Value declarations.

In the Grails controller, the component is used as

class MyController {

  def myComponent

  def someaction() {
    myComponent.doSomething()
  }
}

The result is a NullPointerException i.e. the autowiring of the component didn't work after all. I tried to use @Autowired in the controller, but that gave such a strange problems that I thought it cannot be the road I want to take.

Grails version in use is 2.3.6 The Spring component is also set to use Spring version 3.2.7 to avoid incompatibilities.

UPDATE

Now with again time in my hands for this issue, I set up Spring logging in order to figure out what's happenig. Well, there's plenty of log that Spring context loading produces, but here's what I managed to grep from the whole lot

INFO xml.XmlBeanDefinitionReader Loading XML bean definitions from class path resource [app-context-my-component.xml]
INFO support.PropertySourcesPlaceholderConfigurer Loading properties file from class path resource [my-component.properties]
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public myapp.external.MyComponent myapp.MyService.getMyComponentClient()
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public myapp.external.MyComponent myapp.MyService.getMyComponentClient()
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public myapp.external.MyComponent myapp.MyService.getMyComponentClient()
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public void myapp.MyService.setMyComponentClient(myapp.external.MyComponent)
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public void myapp.MyService.setMyComponentClient(myapp.external.MyComponent)
DEBUG framework.CglibAopProxy Unable to apply any optimisations to advised method: public void myapp.MyService.setMyComponentClient(myapp.external.MyComponent)

I changed the namespaces of the log, but myapp.external namespace refers to the external jar package, and myapp namespace refers to Grails application namespace. I have changed the usage, so that the external component is used from a Service instead of directly from Controller, but that detail had no change in the behaviour.

Per my understanding, the Spring context loading went well.

UPDATE 2

Based on @th3morg's answer I got an idea to try with XML-based config only, skipping all @Component and such annotations from the bean. And it worked! Now Grails manages to import the beans an no longer causes NPE on use.

Although this solves my problem, at least partially. I'm still interested in solution where Spring annotations could be used.

like image 684
kaskelotti Avatar asked Oct 15 '14 03:10

kaskelotti


1 Answers

You should make sure that component scanning is setup in your jar. Checkout out the section titled "Using Spring Namespaces" here http://grails.org/doc/latest/guide/spring.html#theBeanBuilderDSLExplained. You can also use a resources.xml and add component scanning to that file if you can't modify your jar.

Alternatively, you can also wire up the beans one by one, though this is cumbersome and tedious:

beans = {
  myComponent(com.my.MyComponent){
    someOtherService = ref('someOtherService') //if there are other beans to wire by name
    propertyOne = "x"
    propertyTwo = 2
  }
}
like image 94
th3morg Avatar answered Oct 12 '22 13:10

th3morg