3

我有一个基于 Spring 的 Web 应用程序,有两个 servlet——一个用于 MVC,一个用于 spring-ws。应用程序中使用了几个 bean,它们使用注释自动装配。每次应用程序启动时,它都会为每种 bean 类型创建 3 个实例——即使它们是单例范围的。@PostConstruct 方法也被调用了 3 次。

我知道有 3 个应用程序上下文 = 1 个通用 + 2 个 servlet,但是每个 bean、控制器、端点等都被创建了 3 次。至少在父应用程序上下文中加载的公共 bean 应该只实例化一次。

component-scan 的 base-package 属性指向不相交的包。

我使用了一个类来转储上下文信息(https://gist.github.com/1347171),并且似乎存在三个具有相同结构(相同 bean)的不同上下文。他们的 id 是“/project/”、“/project/rest”、“/project/soap”。

我尝试注释掉 ContextLoaderListener,删除soap servlet 及其关联的XML 文件(applicationContext 和soap-servlet)并将常见的东西移动到rest servlet(这样只有一个配置xml 和一个组件扫描),并且我仍然得到每个 bean 的 3 个实例。在这种情况下,应用程序上下文 ID 是“/Project/”(精确大小写)、“/project/”和“/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>

应用程序上下文.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>

肥皂-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>
4

2 回答 2

0

在您的 bean 上使用 javax.ejb.Singleton 注释。

于 2012-10-12T06:02:13.223 回答
0

好吧,原因是关于 spring mvc 上下文初始化和这些神奇的注释默认值的文档有点混乱:(。

由于以下原因,您可能拥有三个副本:

  1. 您的 contextConfigLocation 定义创建了一个在所有 servlet 之间共享的根 webapp 上下文加载(每个应用程序只有一个)。您的每个 -servlet-config.xml 文件都可以访问这些 bean,但反之则不行。 Spring Framework中applicationContext.xml和spring-servlet.xml的区别
  2. 所以第二个实例来自您的 -servlet 应用程序上下文,因为您再次明确定义了注释驱动。
  3. 不确定您使用的是哪个 spring 框架版本,但是通过定义注释驱动和自定义请求映射适配器,您几乎创建了两个。 https://jira.spring.io/browse/SPR-8648

事实上,<mvc:annotation-driven>有所有标签来定制你的适配器,检查你想要的架构。然而,解决这个问题的另一种方法是在跟踪模式下进行痛苦的调试并查看哪个 bean 实际创建了您的适配器。在适配器构造函数中放置一个断点,然后查看 DispatcherServlet->mapperHandler->interceptor->mapping->context->configFileLocation 中的堆栈以查看哪个文件正在创建此 bean

如果您确实想自定义消息转换器,您应该这样做:

<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>  
于 2015-10-19T01:39:12.370 回答