3

I have a Spring-based web application, with two servlets - one for MVC and one for spring-ws. There are several beans used in the application, and they are autowired using annotations. Each time the application starts, it creates 3 instances of each bean type - even though they are singleton-scoped. The @PostConstruct methods are also called three times for each of them.

I understand that there are 3 application contexts = 1 common + 2 servlets, but each bean, controller, endpoint, etc. is created three times. At least the common beans, loaded in the parent application context should be instanced only once.

The base-package attribute of component-scan points to disjoint packages.

I've used a class to dump the context information (https://gist.github.com/1347171) and it appears there are three different contexts with identical structure (same beans). Their id's are "/project/", "/project/rest", "/project/soap".

I tried commenting out the ContextLoaderListener, removing the soap servlet and their associated XML files (applicationContext & soap-servlet) and moving the common stuff into the rest servlet (so that there is only one config xml and only one component-scan), and I still get 3 instances of each bean. In this case the application context id's are "/Project/" (exact casing), "/project/" and "/project/".

web.xml

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

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>

<servlet>
    <servlet-name>rest</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>rest</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

<servlet>
    <servlet-name>soap</servlet-name>
    <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
    <init-param>
        <param-name>transformWsdlLocations</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>soap</servlet-name>
    <url-pattern>/soap/*</url-pattern>
</servlet-mapping>

applicationContext.xml

<context:annotation-config/>
<context:component-scan base-package="test.common"/>

<task:annotation-driven/>

rest-servlet.xml

<mvc:annotation-driven/>
<context:component-scan base-package="test.rest"/>

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <util:list id="beanList">
            <ref bean="formHttpMessageConverter"/>
        </util:list>
    </property>
</bean>

<bean id="formHttpMessageConverter"
      class="org.springframework.http.converter.FormHttpMessageConverter"/>

<mvc:interceptors>
    <bean class="test.rest.Interceptor"/>
</mvc:interceptors>

soap-servlet.xml

<sws:annotation-driven/>
<context:component-scan base-package="test.soap"/>

<sws:dynamic-wsdl
        id="service"
        portTypeName="service"
        locationUri="/soap/service"
        targetNamespace="http://server/soap">

    <sws:xsd location="/WEB-INF/SoapService.xsd"/>
</sws:dynamic-wsdl>

<sws:interceptors>
    <bean id="validatingInterceptor"
          class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
        <property name="schema" value="/WEB-INF/SoapService.xsd"/>
        <property name="validateRequest" value="true"/>
        <property name="validateResponse" value="true"/>
    </bean>
</sws:interceptors>
Mari9
  • 104
  • 2
  • 8
  • What do you mean they are being created three times? Instantiated three times? There three references? I use almost an identical set up as you here and have never noticed any irregular activity. Could you show us whatever evidence is leading you to these conclusions? – thatidiotguy Oct 05 '12 at 16:18
  • They are instantiated three times - and this can be noticed by setting a breakpoint/trace in the constructor. The @PostConstruct-annotated method of each bean is also called three times, which means the instances are 'for real', not just some proxys as this article says could happen: http://amitstechblog.wordpress.com/2011/04/16/spring-singletons-and-lifecycle-annotations/ – Mari9 Oct 08 '12 at 07:50

2 Answers2

0

use the javax.ejb.Singleton annotation on your bean.

Herupkhart
  • 499
  • 4
  • 23
0

Well the reason is the slightly confusing documentation on spring mvc context initialization and these magical annotation defaults :(.

You probably have three copies because of the following:

  1. your contextConfigLocation definition creates a root webapp context loading (only one per application) that is shared across all servlets. Each of your -servlet-config.xml files can access these beans but not vice versa. Difference between applicationContext.xml and spring-servlet.xml in Spring Framework
  2. so the second instance is coming from your -servlet application context because you defined the annotation-driven explicitly again.
  3. Not sure which spring framework version you are using, but by defining both annotation-driven and custom request mapping adapter, you are pretty much creating two. https://jira.spring.io/browse/SPR-8648

In fact, <mvc:annotation-driven> has all the tags to customize your adapter, check the schema which is what you want. Yet anther way to figure this out is by painful debugging in trace mode and looking at which bean is actually creating your adapter. Put a breakpoint in your adapter constructor and then look at the stack in DispatcherServlet->mapperHandler->interceptor->mapping->context->configFileLocation to see which file is creating this bean

If you do want to customize the message converters, you should be doing this:

<mvc:annotation-driven
        content-negotiation-manager="contentNegotiationManager">
            <mvc:message-converters register-defaults="false">
                <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="objectMapper" ref="afterBurnerObjectMapper"/>
                </bean>
                <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.FormHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.StringHttpMessageConverter"/>
            </mvc:message-converters>
    </mvc:annotation-driven>  
Community
  • 1
  • 1
kisna
  • 2,869
  • 1
  • 25
  • 30