views:

10431

answers:

7

I'm using spring 2.5 and annotations to configure my spring-mvc web context. Unfortunately, I am unable to get the following to work. I'm not sure if this is a bug (seems like it) or if there is a basic misunderstanding on how the annotations and interface implementation subclassing works.

For example,

@Controller
@RequestMapping("url-mapping-here")
public class Foo {
  @RequestMapping(method=RequestMethod.GET)
  public void showForm() {
    ...
  }
  @RequestMapping(method=RequestMethod.POST)
  public String processForm() {
  ...
  }
}

works fine. When the context starts up, the urls this handler deals with are discovered, and everything works great.

This however does not:

@Controller
@RequestMapping("url-mapping-here")
public class Foo implements Bar {
  @RequestMapping(method=RequestMethod.GET)
  public void showForm() {
    ...
  }
  @RequestMapping(method=RequestMethod.POST)
  public String processForm() {
  ...
  }
}

When I try to pull up the url, I get the following nasty stack trace:

javax.servlet.ServletException: No adapter for handler [com.shaneleopard.web.controller.RegistrationController@e973e3]: Does your handler implement a supported interface like Controller?
    org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:1091)
    org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:874)
    org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:809)
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571)
    org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:501)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:627)

However, if I change Bar to be an abstract superclass and have Foo extend it, then it works again.

@Controller
@RequestMapping("url-mapping-here")
public class Foo extends Bar {
  @RequestMapping(method=RequestMethod.GET)
  public void showForm() {
    ...
  }
  @RequestMapping(method=RequestMethod.POST)
  public String processForm() {
  ...
  }
}

This seems like a bug. The @Controller annotation should be sufficient to mark this as a controller, and I should be able to implement one or more interfaces in my controller without having to do anything else. Any ideas?

+3  A: 

There's no doubt that annotations and inheritance can get a little tricky, but I think that should work. Try explicitly adding the AnnotationMethodHandlerAdapter to your servlet context.

http://static.springframework.org/spring/docs/2.5.x/reference/mvc.html#mvc-ann-setup

If that doesn't work, a little more information would be helpful. Specifically, are the two annotated controller methods from the interface? Is Foo supposed to be RegistrationController?

Ed Thomas
+3  A: 

Ed is right, adding

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

works fine

Michal Bachman
A: 

Thanks Michal Bachman!

accesine
A: 

I have the same problem in following:

We use advice for our controller

<bean id="myAdvice" class="logging.MyAdvice">
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames">
    <value>myController</value>
  </property>
  <property name="interceptorNames">
    <list>
      <value>myAdvice</value>
    </list>
  </property>
</bean>

where controller has @Controller annotation and implements MyInterface

@Controller
public class MyController implements MyInterface {
...
}

as result get the same error

Servlet.service() for servlet dispatcher threw exception
javax.servlet.ServletException: No adapter for handler [web.MyController@1ecb654]
: Does your handler implement a supported interface like Controller?
        at org.springframework.web.servlet.DispatcherServlet.getHandlerAdapter(DispatcherServlet.java:980)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:762)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:709)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:613)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:525)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:690)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at com.mtvnet.platform.util.HttpServletObjectsHolderFilter.doFilter(HttpServletObjectsHolderFilter.java:46)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at com.mtvi.osiris2.filter.MethodOverrideFilter.doFilterInternal(MethodOverrideFilter.java:64)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at com.mtvi.osiris2.monitoring.MonitorFilter.doFilterInternal(MonitorFilter.java:52)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
        at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:182)
        at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:84)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
        at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:157)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:262)
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:844)
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:446)
        at java.lang.Thread.run(Thread.java:595)

I checked previous advices but it still doesn't work. Can anyone help me to detect problem?

+3  A: 

What I needed to do was replace

 <tx:annotation-driven/>

with

 <tx:annotation-driven  proxy-target-class="true"/>

This forces aspectj to use CGLIB for doing aspects instead of dynamic proxies - CGLIB doesn't lose the annotation since it extends the class, whereas dynamic proxies just expose the implemented interface.

James Kingsbery
A: 

The true reason you need to use 'proxy-target-class="true"' is in DefaultAnnotationHandlerMapping#determineUrlsForHandler() method: though it uses ListableBeanFactory#findAnnotationOnBean for looking up a @RequestMapping annotation (and this takes care about any proxy issues), the additional lookup for @Controller annotation is done using AnnotationUtils#findAnnotation (which does not handles proxy issues)

Boris Kirzner
A: 

I am working with Spring 3.0.x and I am having exactly the same problem: Everything works fine, until I add an interface to my controller-class!

I am using the namespace-configuration, like in the examples in the documentation. But in 3.0.x the -statement does not have a "proxy-target-class"-property anymore!

I tried to push all annotations to interfaces but that did not help either :(

I'm lost!

Kai Moritz
Sorry! The solution (<tx:annotation-driven proxy-target-class="true"/>) still works with 3.0.x -- I just confused <mvc:annotation-driven/> and <tx:annotation-driven/> !!
Kai Moritz