web-dev-qa-db-fra.com

applicationContext ne trouve pas de contrôleurs pour le contexte de servlet

J'ai une application Web Spring avec un fichier applicationContext.xml et une configuration dispatcher-servlet.xml. J'ai défini le <context:component-scan /> dans applicationContext.xml, mais lorsque j'exécute mon application, les contrôleurs ne sont pas trouvés, à moins que j'ajoute également <context:component-scan /> au dispatcher-servlet.xml. J'utilise le même paquet de base dans les deux cas, alors ce n'est pas le problème.

Je suis confus parce que je pensais que le fichier applicationContext.xml était un parent de dispatcher-servlet.xml. Mettre <context:component-scan /> dans applicationContext.xml ne suffirait-il pas?

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://Java.Sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://Java.Sun.com/xml/ns/j2ee http://Java.Sun.com/xml/ns/j2ee/web-app_2_4.xsd">


<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

EDIT: J'utilise également mvc: piloté par l'annotation dans le dispatcher-servlet.xml, qui est supposé prendre les contrôleurs (je pensais?).

EDIT 2: Voici les fichiers de configuration. J'ai supprimé un ensemble de paramètres Spring Security et OAuth de applicationContext.xml (pour des raisons de sécurité et étant donné qu'ils ne sont probablement pas pertinents de toute façon).

applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
      http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<context:component-scan base-package="bar.foo"/>
<context:property-placeholder location="classpath:my.properties" />
<bean class="bar.foo.ServicesConfig" />

</beans>

dispatcher-servlet.xml

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
      http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
      http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

<context:component-scan base-package="bar.foo.controller" />
<mvc:annotation-driven/>
<mvc:default-servlet-handler />

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
    <property name="order" value="2" />
</bean>

<bean id="contentViewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="mediaTypes">
        <map>
            <entry key="json" value="application/json" />
        </map>
    </property>
    <property name="defaultViews">
        <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
    </property>
    <property name="order" value="1" />
</bean>

</beans>

EDIT 3: Ok, c'est intéressant. Mes services et classes de dao sont dans un projet différent (JAR) que je référence depuis le projet Web. J'utilise une configuration basée sur Java et la référence depuis le fichier applicationContext.xml:

<bean class="bar.foo.config.ServicesConfig" />

Cela signifie donc que mon projet Web ne contient que des annotations de contrôleur (où se trouve applicationContext.xml). Rétrospectivement, la suppression du contexte: composant-scan de mon applicationContext.xml ne devrait avoir aucun effet, car il n'y a pas d'annotations à l'exception de celles de @Controller (FIX to EDIT: il existe des annotations @Autowired). Mais, lorsque je supprime le contexte: composant-scan de applicationContext.xml, il est indiqué que les contrôleurs (trouvés dans l'analyse du servlet du distributeur) ne peuvent pas trouver mes classes de service. La référence à ServicesConfig ne devrait-elle pas suffire? Voici la classe ServicesConfig pour les références - elle possède son propre composant d'analyse pour les services, qui est un package différent de celui qu'appliquait applicationContext.xml.

@Configuration
@ComponentScan({ "some.other.package", "another.package" })
@ImportResource({ "classpath:commonBeans.xml" })
@PropertySource({ "classpath:services.properties",
"classpath:misc.properties" })
public class ServicesConfig {
  // Bean definitions //
}

SOLUTION:

Lorsque j'ai supprimé le contexte: analyse de composant de mon contexte racine, les contrôleurs ne prenaient pas les beans de services autowired. En effet, le contexte racine fait référence à mes services Bean config basé sur Java, mais je ne disposais pas de la configuration du contexte racine pour analyser les composants. Par conséquent, lorsque j'ajoute une analyse de composant au contexte racine (applicationContext.xml), tout fonctionne. Voici ce que j'ai maintenant:

applicationContext.xml:

<bean class="bar.foo.config.ServicesConfig" />
<context:component-scan base-package="bar.foo.config" />

dispatcher-servlet.xml:

<context:component-scan base-package="bar.foo.controller" />

J'ai la configuration du contexte Web pour récupérer Controller, Autowired et toutes les autres annotations dans le package du contrôleur. Je ne suis pas sûr que ce soit la meilleure pratique ou non.

21
acvcu

Vous avez raison: il existe deux contextes d'application différents: le contexte d'application racine chargé par ContextLoaderListener (au moment où le ServletContext est initialisé) et le contexte Web (chargé par DispatcherServlet), le contexte d'application racine est le parent du site Web. le contexte.

Maintenant, comme il s’agit de deux contextes d’application différents, ils sont traités différemment - si vous définissez component-scan pour vos services dans un contexte d’application, tous les beans de ces services sont créés ici.

Lorsque votre servlet Dispatcher sera chargé, il commencera à créer le contexte Web (à l'aide de <mvc:annotation-driven/> et créera un mappage pour les méthodes de traitement de votre uri à, il obtiendra la liste des beans dans le contexte de l'application (qui sera le site Web). contexte d'application, pas le contexte d'application racine) et puisque vous n'avez pas défini de component-scan ici, les beans liés au contrôleur ne seront pas trouvés et les mappages ne seront pas créés. C'est la raison pour laquelle vous devez définir une analyse de composant dans le répartiteur servlets contexte également.

Une bonne pratique consiste à exclure les beans liés au contrôleur dans le contexte d'application racine:

<context:component-scan base-package="package">
    <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
</context:component-scan>

et seul le contrôleur lié à celui-ci dans le contexte d'application Web:

<context:component-scan base-package="package" use-default-filters="false">
    <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation" />
</context:component-scan>
34
Biju Kunjummen

Dans nos applications, nous définissons dans le dispatcher-servlet.xml

Je crois que c'est là où il est supposé être plutôt que dans le fichier applicationContext.xml

Cette section de la documentation de Spring devrait fournir plus d'informations:

http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html

Comme vous pouvez le constater à l’un des diagrammes de la section 16.2, le répartiteur-servlet se situe au-dessus du champ applicationContext dans la hiérarchie des contextes. 

1
James Thomas

J'ai eu le même problème et après avoir comparé mon code web.xml avec ce tutoriel je l'ai changé et cela a fonctionné. voici mon fichier web.xml:

<web-app xmlns="http://Java.Sun.com/xml/ns/javaee"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:mvc="http://www.springframework.org/schema/mvc"
     xsi:schemaLocation="
    http://Java.Sun.com/xml/ns/javaee
    http://Java.Sun.com/xml/ns/javaee/web-app_3_0.xsd"
     version="3.0">
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/business-config.xml</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

context-param et listener sont ce que j'ai manqué.

J'espère que ça t'aide.

0