【问题标题】:Spring MVC: Sharing context within earSpring MVC:在耳朵内共享上下文
【发布时间】:2013-04-16 06:51:21
【问题描述】:

我有一个 ear 包,其中包含一个带有通用对象的 jar 和两个我想使用通用 jar 的 war webapp。我已经将配置设置为通过 ContextLoaderListener 和 webapp 上下文分别为 DispatcherServlet 使用应用程序范围的上下文。

我的演示应用的设置大致如下

  • common.jar 包含 applicationContext.xmlbeanRefContext.xml,它们应该是应用程序(ear)范围的上下文。文件如下所示。 shared 命名空间是共享 bean 所在的位置。

applicationContext

<beans>
    <!-- namespace etc declarations omitted -->
    <context:annotation-config />
    <context:component-scan base-package="study.spring.multicontext.shared" />
</beans>

beanRefContext.xml

<beans>
    <!-- namespace etc declarations omitted -->
<bean id="sharedContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
    <constructor-arg>
        <list>
            <value>classpath*:applicationContext.xml</value>
        </list>
    </constructor-arg>
</bean>
</beans>
  • webapp1webapp2 是 Spring MVC 应用程序,使用 web.xml 文件打包为单独的战争,如下所示

    <web-app>
    
    <context-param>
      <param-name>parentContextKey</param-name>
      <param-value>sharedContext</param-value>
    </context-param>
    <context-param>
      <param-name>locatorFactorySelector</param-name>
      <param-value>classpath:beanRefContext.xml</param-value>
    </context-param>
    <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>dos</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/dos-servlet.xml</param-value>
        </init-param>
    
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    
    <servlet-mapping>
        <servlet-name>dos</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

xx-servlet.xml 类似 webapp 特定的上下文。 web 命名空间是控制器所在的位置。

<beans>
    <!-- namespace etc declarations omitted -->

    <context:component-scan base-package="study.spring.multicontext.web"/>
    <mvc:annotation-driven />

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">

      <property name="suffix" value=".jsp"/>
    </bean>

</beans>
  • 共享 bean 在 Controller 类中以正常方式@Autowired

    @Autowired
    MySharedBean mySharedBean
    
  • ear包包含wars和jar,结构类似

    ear
     |
     |--common.jar
     |   |--META-INF
     |   |--applicationContext.xml
     |   |--beanRefContext.xml
     |
     |--webapp1.war
     |   |--WEB-INF
     |       |--xx-servlet.xml
     |       |--web.xml
     |
     |--webapp2.war
     |   |--WEB-INF
     |       |--xx-servlet.xml
     |       |--web.xml
    

问题是仍然会有两个 bean 实例。每个控制器/webapp 一个,因为在每场战争中只有一个控制器。我试图调整配置,但无论我做什么,我要么得到零个实例,要么得到两个实例。

我用 Eclipse MAT 从内存转储中检查了引用,实际上有 4 个实例,但我猜这两个是供 Spring 内部使用的。无论如何,从那里可以清楚地看到每个控制器都有自己的实例。

我已经阅读了许多博客文章、论坛等,他们说这应该像这样简单。有人建议使用 JNDI,但据我所知,没有它应该是可能的。

而且不可能结合战争并将罐子捆绑在里面。由于它可能适用于这个演示应用程序,但我正在使用的真实案例不允许这样做。

非常感谢您对此事的任何帮助。提前致谢。

SpringSource example from 2007 用于 Spring 2.X,其功能相同但配置不同。有点过时,正在寻找基于 Spring 3.X 的解决方案,如赏金描述中所述。

【问题讨论】:

    标签: java spring spring-mvc


    【解决方案1】:

    虽然这个问题是老问题,但如果有人想知道为什么 documented approach 在 Spring Framework 5.0+ 中不起作用。在发布此答案时,Spring Framework 5.0+ 中对耳朵内共享上下文的支持目前已被破坏。 Spring Framework Issue-20805

    上存在一个现有问题

    【讨论】:

      【解决方案2】:

      我们遇到了类似的问题。检查我们为实验创建的这个简单的 maven 示例(EAR 有 2 个 WEB 模块和一个通过父 spring 上下文服务模块共享): EAR with shared spring context between wars

      【讨论】:

      • 查看了您的解决方案。我找不到 spring-share 的 maven 存储库,服务工件。
      【解决方案3】:

      我解决了。

      问题出在类加载中,正如我在 cmets 中对 @Akshay 的回答所怀疑的那样。

      Maven 在每个 war 包中都包含 spring 库,因此它们被多次加载。要解决此问题,需要generate skinny wars

      我认为 Akshay 关于从 web.xml 中的上下文参数中删除 contextConfigLocation 的答案的注释也起到了关键作用。

      【讨论】:

        【解决方案4】:

        就应用程序上下文层次结构而言,我认为从 Spring 2.x 到 3.x 没有任何变化。

        据我所知,您的配置问题在于您正在加载 applicationContext.xml - 加载到 sharedContext 中的那个也正在被每个 webapp 加载,因为它在context-paramcontextConfigLocation中提到。

        由于同一个文件被加载了两次,一次是在父上下文中,一次是在 Web 应用程序的根上下文中,因此会生成副本,以及子上下文,即。 webapp,使用它创建的那些,而不是父级中存在的那些。

        更改您的配置,这样您就不会两次重新加载相同的 beans xml,它应该可以正常工作。您可以同时使用parentContextKeycontextConfigLocation,只是不要加载相同的文件。

        更新: 除了上述之外,您还需要检查共享 jar 是否对 war 可见(在允许共享同一实例时可见。)。我尝试运行博客中的示例,但当我将其部署为 Java EE6 应用程序时,它对我不起作用,这是因为战争中 ear jar 可见性的规则从 Java EE5 更改为 EE6。当我在 Glass Fish 的兼容模式下运行示例时,一切都按预期工作。

        因此,请检查您的 EAR / WAR 以查看您正在运行的 servlet 规范,并确保您的服务器正在相应地部署应用程序。

        如果您必须升级到 Java EE 6,请确保您遵循最新的可见性规则 http://docs.oracle.com/cd/E19226-01/820-7688/gjjdt/index.html。检查战争的MANIFEST 文件,以确保它们具有在Class-Path 配置中明确提到的所有耳罩。

        希望这会有所帮助。

        【讨论】:

        • 感谢您的回复@Akshay。如果我理解正确,您的建议是从 web.xml 文件中删除 contextConfigLocation。我试了一下,结果是 IOException。根据日志,ContextLoaderListener 尝试从 /WEB-INF/ 加载 applicationContext.xml。我认为这就是我首先将位置添加为上下文参数的原因。
        • This blog post 实际上建议在 /WEB-INF/ 中添加一个空的 applicationContext.xml。好吧,我这样做了,应用程序启动正常,但日志显示我仍然有两个共享 bean 实例。其实sharedContext好像被创建了两次。
        • 您可以尝试不使用 contextconfiglocation 和不使用 lovcator 工厂选择器作为 cobtext 参数吗?
        • 我删除了 contextConfigLocationlocatorFactorySelector,但功能保持不变:创建了两个 sharedContext 实例。
        • 哇.. 这是一个脑筋急转弯!这个怎么样..你可以尝试使用spring源博客中的示例应用程序吗?我认为您所要做的就是放入新的弹簧罐中。耳朵应该可以工作。至少这样我们就会知道共享父上下文是可能的,并且可能会帮助您解决特定配置的问题。我会尝试找时间做同样的事情.. 但可能无法很快做到这一点。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-20
        • 1970-01-01
        • 2017-01-27
        • 2016-03-07
        • 2012-07-09
        • 2012-07-18
        相关资源
        最近更新 更多