Votre application Spring MVC standard servira toutes les demandes via un DispatcherServlet
que vous avez enregistré avec votre conteneur Servlet.
Le DispatcherServlet
regarde son ApplicationContext
et, si disponible, le ApplicationContext
enregistré avec un ContextLoaderListener
pour les beans spéciaux dont il a besoin pour configurer sa logique de traitement de demande. Ces beans sont décrits dans la documentation .
Sans doute le type de HandlerMapping
carte le plus important
les demandes entrantes aux gestionnaires et une liste de pré et post-processeurs (intercepteurs de gestionnaires) basée sur certains critères dont les détails varient selon l' HandlerMapping
implémentation. L'implémentation la plus populaire prend en charge les contrôleurs annotés, mais d'autres implémentations existent également.
Le javadoc deHandlerMapping
décrit plus en détail comment les implémentations doivent se comporter.
Le DispatcherServlet
recherche tous les beans de ce type et les enregistre dans un certain ordre (peut être personnalisé). Tout en traitant une requête, le DispatcherServlet
parcourt ces HandlerMapping
objets et teste chacun d'eux avec getHandler
pour trouver celui qui peut gérer la requête entrante, représentée comme le standard HttpServletRequest
. À partir de 4.3.x, s'il n'en trouve aucun , il enregistre l'avertissement que vous voyez
Aucune correspondance trouvée pour la requête HTTP avec l' URI [/some/path]
en DispatcherServlet
avec le nom UnNom
et soit lancers francs un NoHandlerFoundException
ou engage immédiatement la réponse avec un code d'état 404 Not Found.
Pourquoi la DispatcherServlet
recherche n'a-t-elle pas HandlerMapping
pu traiter ma demande?
L' HandlerMapping
implémentation la plus courante est RequestMappingHandlerMapping
, qui gère l'enregistrement des @Controller
beans en tant que gestionnaires (en fait leurs @RequestMapping
méthodes annotées). Vous pouvez soit déclarer vous-même un bean de ce type (avec @Bean
ou <bean>
ou un autre mécanisme), soit utiliser les options intégrées . Ceux-ci sont:
- Annotez votre
@Configuration
classe avec @EnableWebMvc
.
- Déclarez un
<mvc:annotation-driven />
membre dans votre configuration XML.
Comme le lien ci-dessus le décrit, les deux enregistreront un RequestMappingHandlerMapping
bean (et un tas d'autres choses). Cependant, a HandlerMapping
n'est pas très utile sans gestionnaire. RequestMappingHandlerMapping
attend des @Controller
beans, vous devez donc les déclarer également, via des @Bean
méthodes dans une configuration Java ou des <bean>
déclarations dans une configuration XML ou via l'analyse des composants des @Controller
classes annotées dans l'un ou l'autre. Assurez-vous que ces haricots sont présents.
Si vous recevez le message d'avertissement et un 404 et que vous avez configuré tout ce qui précède correctement, vous envoyez votre demande au mauvais URI , qui n'est pas géré par une @RequestMapping
méthode de gestionnaire annotée détectée .
La spring-webmvc
bibliothèque propose d'autres HandlerMapping
implémentations intégrées . Par exemple, des BeanNameUrlHandlerMapping
cartes
des URL aux beans dont les noms commencent par une barre oblique ("/")
et vous pouvez toujours écrire le vôtre. De toute évidence, vous devrez vous assurer que la requête que vous envoyez correspond à au moins un des HandlerMapping
gestionnaires de l' objet enregistré .
Si vous n'enregistrez pas implicitement ou explicitement de HandlerMapping
beans (ou si detectAllHandlerMappings
c'est le cas true
), les valeurs par défaut sontDispatcherServlet
enregistrées . Ceux-ci sont définis dans le même package que la classe. Ils sont et (qui est similaire mais obsolète).DispatcherServlet.properties
DispatcherServlet
BeanNameUrlHandlerMapping
DefaultAnnotationHandlerMapping
RequestMappingHandlerMapping
Débogage
Spring MVC enregistrera les gestionnaires enregistrés via RequestMappingHandlerMapping
. Par exemple, un @Controller
like
@Controller
public class ExampleController {
@RequestMapping(path = "/example", method = RequestMethod.GET, headers = "X-Custom")
public String example() {
return "example-view-name";
}
}
enregistrera ce qui suit au niveau INFO
Mapped "{[/example],methods=[GET],headers=[X-Custom]}" onto public java.lang.String com.spring.servlet.ExampleController.example()
Ceci décrit le mappage enregistré. Lorsque vous voyez l'avertissement qu'aucun gestionnaire n'a été trouvé, comparez l'URI dans le message au mappage répertorié ici. Toutes les restrictions spécifiées dans @RequestMapping
doivent correspondre pour que Spring MVC sélectionne le gestionnaire.
D'autres HandlerMapping
implémentations consignent leurs propres instructions qui devraient faire allusion à leurs mappages et à leurs gestionnaires correspondants.
De même, activez la journalisation Spring au niveau DEBUG pour voir quels beans Spring enregistre. Il doit indiquer les classes annotées qu'il trouve, les packages qu'il analyse et les beans qu'il initialise. Si ceux que vous attendiez ne sont pas présents, vérifiez votre ApplicationContext
configuration.
Autres erreurs courantes
A DispatcherServlet
est juste un Java EE typique Servlet
. Vous l'enregistrez avec votre déclaration typique <web.xml>
<servlet-class>
et <servlet-mapping>
, ou directement via ServletContext#addServlet
un WebApplicationInitializer
, ou avec n'importe quel mécanisme utilisé par Spring boot. En tant que tel, vous devez vous fier à la logique de mappage d'URL spécifiée dans la spécification du servlet , voir le chapitre 12. Voir aussi
Dans cet esprit, une erreur courante consiste à enregistrer le DispatcherServlet
avec un mappage d'URL de /*
, à renvoyer un nom de vue à partir d'une @RequestMapping
méthode de gestionnaire et à s'attendre à ce qu'une JSP soit rendue. Par exemple, considérons une méthode de gestionnaire comme
@RequestMapping(path = "/example", method = RequestMethod.GET)
public String example() {
return "example-view-name";
}
avec un InternalResourceViewResolver
@Bean
public InternalResourceViewResolver resolver() {
InternalResourceViewResolver vr = new InternalResourceViewResolver();
vr.setPrefix("/WEB-INF/jsps/");
vr.setSuffix(".jsp");
return vr;
}
vous pouvez vous attendre à ce que la demande soit transmise à une ressource JSP sur le chemin /WEB-INF/jsps/example-view-name.jsp
. Cela n'arrivera pas. Au lieu de cela, en supposant un nom de contexte de Example
, le DisaptcherServlet
rapportera
Aucun mappage trouvé pour la requête HTTP avec l' URI [/Example/WEB-INF/jsps/example-view-name.jsp]
en DispatcherServlet
avec le nom « dispatcher »
Étant donné que le DispatcherServlet
est mappé vers /*
et /*
correspond à tout (sauf les correspondances exactes, qui ont une priorité plus élevée), le DispatcherServlet
serait choisi pour gérer le forward
depuis le JstlView
(renvoyé par le InternalResourceViewResolver
). Dans presque tous les cas, le DispatcherServlet
ne sera pas configuré pour gérer une telle demande .
Au lieu de cela, dans ce cas simpliste, vous devez enregistrer le DispatcherServlet
to /
, le marquant comme servlet par défaut. Le servlet par défaut est la dernière correspondance pour une requête. Cela permettra à votre conteneur de servlet typique de choisir une implémentation de servlet interne, mappée sur *.jsp
, pour gérer la ressource JSP (par exemple, Tomcat a JspServlet
), avant d'essayer avec le servlet par défaut.
C'est ce que vous voyez dans votre exemple.