Spring MVC: общий контекст в ухе

У меня есть пакет уха, который содержит одну банку с общими объектами и два военных веб-приложения, которые я хотел бы использовать в общей банке. Я настроил конфигурацию для использования широкого контекста приложения через контексты ContextLoaderListener и webapp отдельно для DispatcherServlet.

Настройка моего демонстрационного приложения примерно следующая

  • common.jar содержит applicationContext.xml и beanRefContext.xml, которые должны быть широким контекстом приложения (уха). Файлы выглядят следующим образом. shared namespace - это место, где расположен общий 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>
  • webapp1 и webapp2 являются 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. веб-пространство имен - это место, где расположены контроллеры.

<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 @Autowired обычным образом в классах контроллера

    @Autowired
    MySharedBean mySharedBean
    
  • пакет уха содержит как войны, так и банку, а структура похожа на

    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 с 2007 г. для Spring 2.X, который делает то же самое, но с другой конфигурацией. Немного устарел и ищет решение на основе Spring 3.X, как описано в описании награды.

Ответ 1

Я решил его решить.

Проблема была в загрузке классов, как я подозревал в комментариях к ответу @Akshay.

Maven включил spring libs внутри каждого военного пакета, поэтому они были загружены несколько раз. Чтобы исправить это, нужно генерировать тощие войны.

Я предполагаю, что Akshay отмечает, что его ответ на удаление contextConfigLocation из контекстных параметров в web.xml также был ключевым.

Ответ 2

Я не верю, что что-то изменилось с Spring 2.x на 3.x в отношении иерархии контекстов приложения.

Из того, что я могу сказать, проблема с вашей конфигурацией заключается в том, что вы загружаете applicationContext.xml - тот, который загружен в sharedContext, также загружается каждым webapp, из-за того, что его упомянутых в context-param contextConfigLocation.

Поскольку один и тот же файл загружается дважды, один раз в родительском контексте и один раз в корневом контексте веб-приложения, есть сделанные копии и дочерний контекст, т.е. webapp, использует те, которые он создал, а не те, которые присутствуют в родительском.

Измените свою конфигурацию, чтобы дважды не перезагружать один и тот же beans xml, и он должен работать нормально. Вы можете использовать parentContextKey и contextConfigLocation как просто не загружать те же файлы.

Обновление: В дополнение к вышесказанному, вам также необходимо проверить, видима ли общая банка для войн (видимая, как в разрешении на использование одного и того же экземпляра). Я попытался запустить образец из блога, и это не сработало для меня, когда я развернул его как приложение Java EE6, и это потому, что правила для видимости ящика для уха в войнах изменились с Java EE5 на EE6. Когда я запускаю образец в режиме совместимости Glass Fish, все работает так, как ожидалось.

Итак, проверьте свои EAR/WARs, чтобы узнать, какие функции сервлета вы используете, и убедитесь, что ваш сервер использует приложение соответствующим образом.

Если вам нужно перейти на Java EE 6, убедитесь, что вы следуете последним правилам видимости http://docs.oracle.com/cd/E19226-01/820-7688/gjjdt/index.html. Проверьте файлы MANIFEST войн, чтобы убедиться, что они имеют все ушные банки, явно упомянутые в конфигурации Class-Path.

Надеюсь, что это поможет.

Ответ 3

У нас была аналогичная проблема. Проверьте этот простой пример maven (EAR с 2 WEB-модулями и общим через родительский spring контекстный сервисный модуль), который мы создали для эксперимента: EAR с общим spring контекстом между войнами

Ответ 4

Хотя этот вопрос старый, если кому-то интересно, почему документированный подход не работает в Spring Framework 5. 0+. Поддержка совместного использования контекста в ухе в настоящее время нарушена в Spring Framework 5. 0+ на момент публикации этого ответа. Существует открытая проблема в Spring Framework Issue-20805