Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
157 views
in Technique[技术] by (71.8m points)

java - Spring MVC: Sharing context within ear

I have an ear package that contains one jar with common objects and two war webapps that I'd like to use the common jar. I've setup the configuration to use application wide context via ContextLoaderListener and webapp contexts separately for DispatcherServlet.

The setup of my demo app is roughly the following

  • common.jar contains applicationContext.xml and beanRefContext.xml, which are supposed to be application (ear) wide context. The files are like below. shared namespace is where the shared bean is located.

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>
  • webapp1 and webapp2 are Spring MVC applications packaged as separate wars with web.xml file like below

    <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>
    

and xx-servlet.xml like for webapp specific context. web namespace is where the controllers are located.

<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>
  • The shared bean is @Autowired in normal fashion in Controller classes

    @Autowired
    MySharedBean mySharedBean
    
  • ear package contains both wars and jar, and structure is like

    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
    

The problem is that there will still be two instances of the bean. One for each controller/webapp, since there's only one Controller in each of the wars. I have tried to twiddle with the configuration, but no matter what I do, I either get zero instances or two instances.

I checked the references with Eclipse MAT from a memory dump, and there are actually 4 instances, but I guess the two are for Spring internal use. Anyway, from there it's clearly visible that each controller has it's own instance.

I've read numerous of blog posts, discussion forums, etc where they say that this should be as simple as this. Some suggest JNDI, but as I've understood, this should be possible without it.

And it's not possible to combine the wars and bundle the jar inside. As it might work for this demo app, the real life case I'm working with does not allow this.

Any help on this matter is highly appreciated. Thanks in advance.

SpringSource example from 2007 for Spring 2.X that does the same but with different configuration. A bit outdated and looking for a Spring 3.X based solution, as dscribed in the bounty description.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I do not believe anything has changed from Spring 2.x to 3.x as far as application context hierarchies are concerned.

From what I can tell, the issue with your config is that you are are loading the applicationContext.xml - the one which is loaded into the sharedContext, is also being loaded by each webapp, because of the fact that its mentioned in the context-param contextConfigLocation.

Since the same file is loaded twice, once in the parent context and once in the web application's root context, there are copies made, and the child context, ie. webapp, uses the ones it created, not the ones that are present in the parent.

Change your config so you don't reload the same beans xml twice, and it should work fine. You can use parentContextKey and contextConfigLocation both just don't load the same files.

Update: In addition to the above, you also need to check if the shared jar is visible to the wars (visible as in allowed to share the same instance.). I tried to run the sample from the blog and it did not work for me when I deployed it as a Java EE6 application, and that's because the rules for ear jar visibility inside wars changed from Java EE5 to EE6. When I run the sample in compatibility mode of Glass Fish, everything works as expected.

So check your EAR / WARs to see what servlet spec you are running, and make sure your server is deploying the application accordingly.

If you have to upgrade to Java EE 6, make sure you are following the latest visibility rules http://docs.oracle.com/cd/E19226-01/820-7688/gjjdt/index.html. Check the MANIFEST files of the wars to ensure they have all ear jars explicitly mentioned in the Class-Path configuration.

Hope this helps.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...