Search code examples
grailsclasscastexceptionautowiredgrails-controller

Why does using an action name that is the same as a configured SpringBean name result in a ClassCastException?


To reproduce this problem use the following steps.

  1. Create a new Grails application.
  2. Create a new controller called FooController
  3. Add an action "bar" to FooController
  4. In src/groovy, create a new class called Bar
  5. In resources.groovy configure a SpringBean called bar

    bar(Bar) {bean -> bean.autowire = 'byName' }

  6. Start the application and navigate to http:localhost:8080/[appContext]/foo/bar
  7. You should get a stacktrace similar to this:

java.lang.ClassCastException: Bar cannot be cast to groovy.lang.Closure at org.grails.plugin.resource.DevModeSanityFilter.doFilter(DevModeSanityFilter.groovy:44) at java.lang.Thread.run(Thread.java:680)'

Why is this occuring? Is it a bug in Grails or expected behaviour?

I would expect that there should not be name clashes between configured SpringBeans and action names.


Solution

  • The problem is that Groovy syntax like

    class FooController {
      def bar = {
        // do something
      }
    }
    

    gives the FooController class two public methods

    public Object getBar() {
      return bar;
    }
    
    public void setBar(Object newBar) {
      bar = newBar;
    }
    

    The existence of the setBar method makes Spring consider it as a property to be autowired, and it replaces the closure value with your bean. Grails itself only requires the getter method, so if instead you say

    class FooController {
      final bar = {
        // do something
      }
    }
    

    (i.e. declare bar to be final) then Groovy will synthesize only the getter and not the setter, and Spring will not see bar as a property it can autowire.