Spring-MVC Problem using @Controller on controller implémentation d'une interface

j'utilise spring 2.5 et annotations pour configurer mon contexte Web spring-mvc. Malheureusement, je ne parviens pas à obtenir la travailler. Je ne suis pas sûr qu'il s'agisse d'un bug (semble-t-il) ou s'il y a un malentendu fondamental sur la façon dont les annotations et l'implémentation de l'interface fonctionnent.

Par exemple,

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

fonctionne très bien. Lorsque le contexte démarre, les urls traitées par ce gestionnaire sont découvertes, et tout fonctionne parfaitement.

toutefois, Cela n'est pas:

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

quand j'essaie de remonter l'url, j'obtiens la trace suivante de la pile:

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)

cependant, si je change la barre pour être une superclasse abstraite et que Foo l'étende, alors cela fonctionne à nouveau.

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

cela ressemble à un bug. L'annotation @Controller devrait être suffisante pour marquer ceci en tant que controller, et je devrais être capable d'implémenter une ou plusieurs interfaces dans mon controller sans avoir à faire quoi que ce soit autre. Des idées?

33
demandé sur leppie 2008-09-30 21:09:51

6 réponses

Ed a raison, ajoutant

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

fonctionne très bien

12
répondu Michal Bachman 2010-03-01 20:11:17

Ce que je devais faire était de remplacer

 <tx:annotation-driven/>

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

cela force aspectj à utiliser CGLIB pour faire des aspects au lieu de proxies dynamiques-CGLIB ne perd pas l'annotation puisqu'il étend la classe, alors que les proxies dynamiques exposent simplement l'interface implémentée.

12
répondu James Kingsbery 2010-07-06 22:13:48

si vous souhaitez utiliser des interfaces pour vos contrôleurs MVC de printemps, alors vous devez déplacer les annotations un peu, comme mentionné dans les Docs de printemps: http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping

L'utilisation de @RequestMapping sur les méthodes D'Interface un piège commun quand travailler avec des classes de controller annotées se produit lors de l'application une fonctionnalité qui nécessite la création d'un proxy objet de contrôleur (par exemple, méthodes @transactionnelles). Habituellement vous introduirez une interface pour le contrôleur afin d'utiliser JDK dynamic proxies. Pour faire de cette vous devez déplacer les annotations @RequestMapping à l'interface comme bien que le mécanisme de cartographie ne peut "voir" l'interface exposée par proxy. Vous pouvez aussi activer proxy-target-class= " true" dans la configuration de la fonctionnalité appliquée au contrôleur (dans notre transaction scénario ). Faire indique que les procurations de la sous-classe basées sur CGLIB doivent être utilisées au lieu de JDK proxies basé sur l'interface. Pour plus d'informations sur les différents proxy mécanismes voir la Section 8.6, "mécanismes de remplacement".

Malheureusement, il ne donne pas un exemple concret. J'ai trouvé une configuration comme ceci fonctionne:

@Controller
@RequestMapping(value = "/secure/exhibitor")
public interface ExhibitorController {

    @RequestMapping(value = "/{id}")
    void exhibitor(@PathVariable("id") Long id);
}

@Controller
public class ExhibitorControllerImpl implements ExhibitorController {

    @Secured({"ROLE_EXHIBITOR"})
    @Transactional(readOnly = true)
    @Override
    public void exhibitor(final Long id) {

    }
}

donc ce que vous avez ici est une interface qui déclare les annotations @Controller, @PathVariable et @RequestMapping (les annotations MVC du printemps) et puis vous pouvez mettre vos annotations @Transactional ou @Secured par exemple sur la classe de béton. Ce ne sont que les annotations de type @Controller que vous devez mettre sur l'interface à cause de la façon dont Spring fait ses correspondances.

Notez que vous ne devez le faire que si vous utilisez une interface. Vous n'avez pas nécessairement besoin de le faire si vous êtes satisfait de CGLib proxies, mais si pour une raison quelconque vous voulez utiliser JDK dynamic proxies, ce pourrait être la voie à suivre.

10
répondu Kieran 2012-10-23 11:10:56

il n'y a aucun doute que les annotations et l'héritage peuvent devenir un peu délicat, mais je pense que cela devrait fonctionner. Essayez d'ajouter explicitement L'AnnotationMethodHandlerAdapter à votre contexte servlet.

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

Si cela ne fonctionne pas, un peu plus d'information serait utile. Plus précisément, les deux méthodes de contrôle annotées proviennent-elles de l'interface? De Foo censé être RegistrationController?

5
répondu Ed Thomas 2008-12-10 06:22:49

je sais qu'il est trop tard mais j'écris ceci pour quelqu'un qui a ce problème si vous utilisez la configuration basée sur les annotations... la solution pourrait être comme ceci:

@Configuration
@ComponentScan("org.foo.controller.*")
@EnableAspectJAutoProxy(proxyTargetClass=true)
public class AppConfig { ...}
3
répondu Amir 2016-04-13 06:55:04

La vraie raison pour laquelle vous devez utiliser l'option " proxy-cible class="true"'DefaultAnnotationHandlerMapping#determineUrlsForHandler() méthode: si elle utilise ListableBeanFactory#findAnnotationOnBean pour la recherche d'un @RequestMapping annotation (et cela prend soin des problèmes de proxy), la recherche supplémentaire pour @Controller l'annotation est faite en utilisant AnnotationUtils#findAnnotation (qui n'a pas les poignées de la procuration)

0
répondu Boris Kirzner 2010-09-05 08:46:25