我们的团队有相同的要求——在 Tomcat 中的多个 WAR 之间共享 Spring bean,老实说,诸如“不要这样做”之类的答案没有帮助。
该要求源于这样一个事实:我们有一个在 Tomcat 上运行的多 WAR 应用程序,并且所有 WAR 都需要访问相同的 RDBMS 来持久保存信息。我们使用 Spring 和 Hibernate 来访问 RDBM,并且所有 WAR 共享相同的模式,并且理想情况下可以使用相同的 Hibernate SessionFactory 和 Spring 事务管理器。
关于如何做到这一点的答案已发布在这里:
StackOverflow:在 EAR 中共享 ApplicationContext
总而言之,您在 web.xml 中执行以下操作:
<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:yourWarSpecificAppContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
其中 beanRefContext.xml 包含:
<beans>
<bean id="sharedContext" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>classpath:yourSharedAppContext.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
这使用了SpringContextSingletonBeanFactoryLocator公开并共享父上下文(在本例中使用名称“sharedContext”)。当第一个 WAR 引用共享上下文时,将延迟加载它。
无论您在共享上下文中引用什么 bean,都必须可供所有 WAR 访问,这意味着它们无法从特定 WAR 中的 WEB-INF/classes 或 WEB-INF/lib 加载。它们必须被共享,要么使用 EAR 文件,要么将包含 bean(和依赖项)的 jar 放在 Tomcat 共享“lib”文件夹($CATALINA_HOME/lib)中,这就是我们团队所做的。
公平警告,如果您使用此方法,您的大部分 JAR 可能位于共享 lib 文件夹中,而不是单个 Web 应用程序中。对于我们的项目来说,这是有意义的,因为大多数 Web 应用程序共享并访问相同的后端服务。
由于核心 Tomcat 开发人员可能会反对将大量代码放入 Tomcat 共享 lib 目录中,因此我将仅列举其他建议答案可能不起作用的一些原因。
- 为每个 WAR 使用单独的应用程序上下文意味着有多个到数据库的连接池,每个 WAR 一个,并且为每个 WAR 单独初始化昂贵的 Hibernate SessionFactory,这会增加服务器启动时间和内存消耗。更一般地说,它不允许在同一 Tomcat 中运行的 Web 应用程序之间共享共享后端服务的状态。
- 将持久性代码放入单独的 WAR 中并使用 REST 调用(至少在我们的例子中)对于开发人员来说完全不方便,并且与直接调用共享 bean 相比,增加了访问数据库的路径长度。