30

I have a problem with my Spring application where my @Service classes are being created twice when the application starts. I know this is a problem with my configuration, as I've experienced it before, but what exactly am I doing wrong?

Is there anything fundamentally wrong with the way I've laid out my config, below? (I have omitted everything I deem to be irrelevant)

web.xml:

<servlet>
    <servlet-name>myapp</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>myapp</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/myapp-config.xml
        /WEB-INF/myapp-security.xml
        /WEB-INF/myapp-mvc.xml
    </param-value>
</context-param>

<listener>
    <listener-class>com.myapp.servlet.MyAppContextListener</listener-class>
</listener>

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

myapp-servlet.xml

<context:component-scan base-package="com.myapp" annotation-config="true" />
<mvc:annotation-driven />

myapp-config.xml

<context:component-scan base-package="com.myapp" annotation-config="true" />
<context:annotation-config />
Steve
  • 2,207
  • 6
  • 24
  • 35
  • 2
    _As a side note_: no need for `` if you already use ``. Very nice and detailed explanation can be found here: [Difference between vs ](http://stackoverflow.com/a/7456501/814702) – informatik01 May 12 '15 at 20:06

3 Answers3

35

In addition to @GaryF's answer, there is a following beautiful solution for this problem (used in projects generated by Spring Roo):

myapp-config.xml

<!-- Load everything except @Controllers -->
<context:component-scan base-package="com.myapp">
    <context:exclude-filter expression="org.springframework.stereotype.Controller"
        type="annotation"/>
</context:component-scan>

myapp-servlet.xml

<!-- Load @Controllers only -->
<context:component-scan base-package="com.myapp" use-default-filters="false">
    <context:include-filter expression="org.springframework.stereotype.Controller" 
        type="annotation"/>
</context:component-scan>

EDIT:

Removing <context:component-scan> from myapp-config.xml means, that all your autodiscovered annotated beans are registered in DispatcherServlet's context (that is, the context loaded from myapp-servlet.xml).

However the recommended approach is to use servlet's context for presentation-specific things (such as controllers), and use the root context (myapp-config.xml) for the core services of your application. The solution above make this approach easy.

Regarding the practical considerations, when your core services are placed in servlet's application context, they can't be accessed from outside the scope of that servlet, for example, from another servlets (you may need to use another servlets to implement another access technologies) or filters (such as Spring Security filters). That's the reason for having core services in the root application context.

Community
  • 1
  • 1
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • 1
    Right now I don't use it at all in myapp-config.xml and everything seems to work. What's the benefit of your solution? – Steve Dec 02 '10 at 14:46
  • This works very well. To anyone else who implements it, you may need to define Spring Security's inside your servlet.xml – Steve Dec 08 '10 at 15:40
  • That's quite a clever solution. – GaryF Dec 08 '10 at 16:12
15

As an addition to the answer @axtavt gave, I’d like to give the matching Spring JavaConfig here.

In RootConfig.java:

@ComponentScan(basePackages = { "com.myapp" },
    excludeFilters = @Filter({Controller.class, Configuration.class}))

In WebMvcConfig.java:

@ComponentScan(basePackages = { "com.myapp" },
    useDefaultFilters = false, includeFilters = @Filter(Controller.class))
Michael Piefel
  • 18,660
  • 9
  • 81
  • 112
9

You're doing two separate component-scans over the same base-package. Remove one of them.

GaryF
  • 23,950
  • 10
  • 60
  • 73
  • Looks like I need in in -servlet, or else nothing really works. I've removed it from -config and everything seems to be back to normal! – Steve Dec 02 '10 at 09:38
  • But aren't instances created by Spring singleton by default? Or is is true only within the same ApplicationContext? – Abhijeet Kashnia Dec 02 '10 at 10:33
  • 2
    @Abhijeet - Yes, they're singletons inside the same context (by default). These are two discrete contexts. – GaryF Dec 02 '10 at 10:34