在 Tomcat 中将配置与 WAR 分离的优雅方法

Posted

技术标签:

【中文标题】在 Tomcat 中将配置与 WAR 分离的优雅方法【英文标题】:Elegant ways to separate configuration from WAR in Tomcat 【发布时间】:2010-12-15 17:54:39 【问题描述】:

我正在尝试找到在 Tomcat 中运行的 Spring webapp 中传递复杂配置的最佳方法。目前我使用 JNDI 将数据源和字符串从 Tomcat 上下文传递到 webapp,这很好用。

但是,假设我需要选择通知服务的实现。 Spring 无法有条件地选择要实例化的 bean(尽管过去我使用 JNDI 字符串通过设置 contextConfigLocation 来导入预定义的 bean 配置)。

我还看到许多 webapps 提供了一个配置工具来创建一个自定义的 WAR 文件。在我看来,这是一种不好的形式,如果没有其他原因,它会阻止从上游重新部署 WAR,而无需进行许多检查以确保重新应用所有配置。

理想情况下,我可以提供一个 Spring XML 文件,该文件存在于 webapp 之外的文件系统中。但是,spring import 指令似乎无法解析 $ 变量,因此无法提供自定义。

我可以在这里使用任何技术来正确地将复杂的配置与 web 应用程序分开吗?

【问题讨论】:

【参考方案1】:

如果我想要配置一组特定的 bean,并且此配置必须与 WAR 文件分开,我通常会执行以下操作:

applicationContext.xml 中:

<!-- here you have a configurer based on a *.properties file -->
<bean id="configurer" 
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="location" value="file://$configDir/configuration.properties"/>
    <property name="ignoreResourceNotFound" value="false" />
    <property name="ignoreUnresolvablePlaceholders" value="false" />
    <property name="searchSystemEnvironment" value="false" />
</bean>

<!-- this is how you can use configuration properties -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
    <property name="host" value="$smtp.host"/>
</bean>

configuration.properties中:

smtp.host=smtp.your-isp.com

您还需要使用 -DconfigDir=/path/to/configuration/directory 启动 Tomcat

【讨论】:

在属性文件中使用classpath: URI 是不是更“优雅”,所以您可以将它放在类路径中的某个位置,避免摆弄 Tomcat 启动参数?跨度> 嗯,有没有办法在 Tomcat 类路径中获取一些东西,而不会将它放在 WAR 中?我知道有 shared/lib 或 common/lib 是否有等价的类? 请注意,在 Tomcat 6.0 中它是 $catalina.home/lib。感谢您的帮助,我想我会成功的。【参考方案2】:

如果您使用的是 Spring 3,则可以利用 Spring 表达式语言。假设您有两个应用程序 app1.war 和 app2.war,它们需要一个名为 config.properties 的属性文件。应用程序将使用上下文路径 /app1 和 /app2 进行部署。

在一个公共目录中创建两个目录 app1 和 app2,例如。 C:\myConfig\app1 和 C:\myConfig\app2。

将 config.properties 放在 app1 中,另一个 config.properties 放在 app2 中。

然后创建一个 $CATALINA_HOME/conf/[enginename]/[hostname]/context.xml.default 文件,内容如下:

context.xml.default:

<Context>
  <Parameter name="myConfigDirectory" value="C:/myConfig" override="false"/>
</Context>

参数 myConfigDirectory 将对主机上的所有应用程序可用。最好在 context.xml.default 中而不是在 server.xml 中创建此参数,因为稍后可以更改文件而无需重新启动 tomcat。

在war里面的applicationContext.xml中你可以使用SpEL表达式访问config.properties:“#contextParameters.myConfigDirectory + servletContext.contextPath/config.properties”,例如你可以这样写:

applicationContext.xml:

<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="location" value="file:#contextParameters.myConfigDirectory + servletContext.contextPath/config.properties" />
</bean>

对于具有 contextPath /app1 的应用程序,表达式将扩展为 C:/myConfig/app1,对于具有 contextPath /app2 的应用程序,该表达式将扩展为 C:/myConfig/app2。这将使应用程序根据其 contextPath 访问 config.properties 文件。

【讨论】:

【参考方案3】:

如果您想在 Web 容器之间完全可移植,则不能依赖 WAR 文件之外的任何内容。在 Tomcat 中,SecurityManager 允许您发现磁盘上部署代码的物理位置,然后您可以使用该知识将磁盘导航到放置配置文件的位置。

参见例如Determine location of a java class loaded by Matlab

【讨论】:

以上是关于在 Tomcat 中将配置与 WAR 分离的优雅方法的主要内容,如果未能解决你的问题,请参考以下文章

在tomcat中将相同的war文件部署为两次不同的路径

linux中将war包放入tomcat的web apps,访问tomcat是成功的,但访问jsp文件就报404,怎么办在线急等

如何创建 Spring Boot 应用程序并在 Tomcat 中将其作为 .war 运行? [复制]

在eclipse中将GWT应用程序部署到tomcat

在tomcat 7中设置默认应用程序

AWS Elastic Beanstalk:Tomcat 忽略了我的 WAR 文件