2008-09-30 20 views
32

J'utilise spring 2.5 et des annotations pour configurer mon contexte web spring-mvc. Malheureusement, je suis incapable de faire fonctionner ce qui suit. Je ne suis pas sûr que ce soit un bug (semble-t-il) ou s'il y a un malentendu fondamental sur le fonctionnement des annotations et du sous-classement de l'implémentation de l'interface.Spring-MVC Problème lors de l'utilisation de @Controller sur un contrôleur implémentant une interface

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 URL que ce gestionnaire manipule sont découvertes et tout fonctionne bien.

Cela ne signifie toutefois 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() { 
    ... 
    } 
} 

Lorsque je tente de tirer vers le haut l'url, je reçois la mauvaise trace de la pile suivante:

javax.servlet.ServletException: No adapter for handler [[email protected]]: 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 barre à un résumé superclasse et Foo l'étend, alors ça marche à 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 semble être un bogue. L'annotation @Controller devrait être suffisante pour marquer cela comme un contrôleur, et je devrais être en mesure d'implémenter une ou plusieurs interfaces dans mon contrôleur sans avoir à faire quoi que ce soit d'autre. Des idées?

Répondre

5

Il ne fait aucun doute que les annotations et l'héritage peuvent être un peu difficiles, mais je pense que cela devrait fonctionner. Essayez d'ajouter explicitement AnnotationMethodHandlerAdapter à votre contexte de 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'informations serait utile. Plus précisément, les deux méthodes de contrôleur annotées proviennent-elles de l'interface? Est-ce que Foo est censé être RegistrationController?

12

Ed est juste, en ajoutant

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

fonctionne très bien

12

ce que je devais faire était remplacer

<tx:annotation-driven/> 

avec

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

Cette forces AspectJ cglib pour faire des aspects au lieu de dy proxies namic - CGLIB ne perd pas l'annotation car elle étend la classe, alors que les proxies dynamiques n'exposent que l'interface implémentée.

+0

Needed de noter que CGLIB est plutôt obsolète. –

0

La vraie raison pour laquelle vous devez utiliser « proxy cible class = « true » » est en DefaultAnnotationHandlerMapping#determineUrlsForHandler() méthode: si elle utilise ListableBeanFactory#findAnnotationOnBean pour la recherche une annotation @RequestMapping (et cela prend en charge sur les problèmes de proxy), la plus recherche pour @Controller annotation est effectuée à l'aide AnnotationUtils#findAnnotation (qui ne traite des questions de proxy)

+0

Voulez-vous dire qu'il y a un bug? – Ruslan

10

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

Utilisation @RequestMapping sur les méthodes d'interface Un problème fréquent lorsque fonctionne avec des classes de contrôleur annotées se produit lors de l'application de la fonctionnalité qui nécessite la création d'un proxy pour l'objet contrôleur (par ex. @ Méthodes transactionnelles). Généralement, vous allez introduire une interface pour le contrôleur afin d'utiliser les proxys dynamiques JDK. Pour que ce fonctionne, vous devez déplacer les annotations @RequestMapping vers l'interface en tant que , car le mécanisme de mappage ne peut "voir" que l'interface exposée par le proxy. Vous pouvez également activer proxy-target-class = "true" dans la configuration de la fonctionnalité appliquée au contrôleur (dans notre scénario de transaction). indique que les proxies de sous-classes basées sur CGLIB doivent être utilisés à la place des proxies JDK basés sur l'interface. Pour plus d'informations sur les différents mécanismes , reportez-vous à la Section 8.6, «Mécanismes de transmission par la force».

Malheureusement, cela ne donne pas un exemple concret de ceci. J'ai trouvé une configuration comme cela 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 la @Controller, @PathVariable et annotations @RequestMapping (les annotations Spring MVC) et vous pouvez mettre votre @ Des annotations transactionnelles ou @Secured par exemple sur la classe concrète. Ce sont uniquement les annotations de type @Controller que vous devez placer sur l'interface en raison de la manière dont Spring effectue ses mappages.

Notez que vous n'avez besoin de le faire que si vous utilisez une interface. Vous n'avez pas forcément besoin de le faire si vous êtes satisfait des proxys CGLib, mais si pour une raison quelconque vous souhaitez utiliser des proxys dynamiques JDK, cela pourrait être la solution.

2

Je sais qu'il est trop tard, mais j'écris ceci pour tous ceux qui ont ce problème si vous utilisez la configuration à base d'annotation ... la solution pourrait ressembler à ceci:

@Configuration 
@ComponentScan("org.foo.controller.*") 
@EnableAspectJAutoProxy(proxyTargetClass=true) 
public class AppConfig { ...}