我可以在多个环境中使用单个war文件吗? 我是不是该?

我的工作中有一个Java Web应用程序,我想简化我们部署到DEV,QA和PROD环境的方式。

应用程序在启动时读取一系列属性,dev,qa和prod的属性文件不同。 每当我想部署到某个环境时,我都会将特定于环境的属性文件放到我的app文件夹中,构建war,然后将其部署到三个tomcat 5.5服务器之一。

我想要做的是必须有一个.war,它具有所有环境的属性,并让应用程序在初始化过程中询问Web服务器,以确定应用程序所处的环境,从而确定要加载的属性。 是否有一种简单的方法(或者说,这是一种标准的方式)?

这实际上取决于您使用这些属性的内容。

一些(例如数据源)可以在容器本身中配置( Tomcat 5.5.JNDI Resources ,也参见JDBC sources部分)。

其他(特定于应用程序)可能确实需要属性。 在这种情况下,您的选择是:

  1. 捆绑WAR文件中的属性并根据某些外部开关(环境变量或JVM属性)加载适当的子集
  2. 在解压缩war的每个服务器上设置部署过程,并将属性文件(位于该服务器上的预定义位置并且特定于该服务器)复制到WEB-INF/classes (或其他适当的位置)。

至于“这是一个理想的目标” – 是的,我想是的。 在QA /分期中测试单个WAR然后部署到生产中会减少中间步骤,从而减少出错的机会。

更新 (根据评论):

上面的项目#1指的是一个实际的环境变量(例如你通过SET ENV_NAME=QA Windows中的SET ENV_NAME=QAENV_NAME=QA; export ENV_NAME在Linux中ENV_NAME=QA; export ENV_NAME )。 您可以使用System.getenv()从代码中读取其值并加载相应的属性文件:

 String targetEnvironment = System.getenv("TARGET_ENV"); String resourceFileName = "/WEB-INF/configuration-" + targetEnvironment + ".properties"; InputStream is = getServletContext().getResourceAsStream(resourceFileName); Properties configuration = new Properties(); configuration.load(is); 

但是,您可以改为通过JNDI定义标量值( 请参阅Tomcat doc中的环境条目 ):

    

并通过您的应用程序阅读它

 Context context = (Context) InitialContext().lookup("java:comp/env"); String targetEnvironment = (String) context.lookup("TARGET_ENV"); // the rest is the same as above 

问题是,如果您仍将使用JNDI,您可以放弃您的属性文件并通过JNDI配置所有内容。 您可以使用您的数据源作为实际资源,基本属性仍然是标量(尽管它们是类型安全的)。

最终由您来决定哪种方式更适合您的特定需求; 两者都有利有弊。

你所做的是一场等待发生的事故……有一天,DEV战争将最终进入de PROD服务器,并且某些法律优于所有自然法则,这些问题将在凌晨2点被发现。 无法解释为什么会这样,但有一天会发生。 因此,在我看来,一场战争肯定是一个好主意。

您可以在相应的JVM中设置系统属性(-Dcom.yourdomain.configpath = / where / you / store / configfiles)并获取此属性

 String value = System.getProperty("com.yourdomain.configpath", "defaultvalue_if_any"); 

默认值可以指向战争中的某个位置(WEB-INF / …),或者如果没有默认值,则可以在加载期间使用一些日志记录噪声来警告错误配置。 还要注意,这种技术不依赖于平台,所以你的dev机器可以是Windows机器,服务器是Linux机器,它可以应付这两者。 我们通常在此配置路径中为每个应用程序创建一个子目录,因为有几个应用程序在服务器上使用此系统,我们希望保持整洁。

作为一个额外的好处,你不会冒这种方式在PROD服务器上手动调整属性文件的风险。 只是不要忘记在备份方案中包含文件存储的路径。

我认为单个war文件是一个很好的方法,因为很高兴有信心你在DEV中测试的二进制文件与Production中的完全相同。 我们这样做的方式,我们将配置保存在战争之外的单独属性文件中,但是在app服务器的类路径中。

如果你想保持战争中的所有属性(这确实使部署更容易,因为那时你不必也部署属性文件),你可以在类路径中保留一个标识服务器环境类型的属性文件,并使用它来键入.war文件中属性文件中的值。 外部属性文件也可能是一种很好的方法,可能会有一些高级配置,这些配置不会发生太大变化,并且可以在多个war文件中使用。

可能最简单的方法是设置应用程序服务之间不同的环境变量,并使用它来确定要读取的属性文件。

另一种可能性是将属性存储在数据库中,并使用存在于标准JNDI名称下的数据源,但指向不同环境中的不同位置。

我更喜欢一个EAR或一个WAR方法。 从安全的角度来看,有一些重新保证并经常要求采用刚刚测试的完全相同的EAR并将其直接移动到下一个状态(测试>生产)。

除容器提供的属性文件外,还有许多选项。 通常,容器有一个很好的用户界面,可以在你死了之后保持这些价值和资源。

有无数使用数据库支持的ResourceBundle的例子。

例如,Spring框架有几种机制可以在不费力的情况下实现。 从PropertyPlaceholderConfigurer开始

在启动时设置一个指向属性文件位置的Systemproperty,然后在您的应用程序中拉入此属性并加载您的设置。 我做的另一件事是有两个属性文件,比如default.properties和external.properties。 它们包含相同的属性,但default.properties包含默认值(大多数时间设置),此文件在战争中。 然后,如果你部署到env。 你找到了external.properties,如果找到了,那么你就会回滚到default.properties。 如果需要,这提供了覆盖属性的好方法,但也有默认设置。 这适用于我的许多部署,但可能不在您的方案中。

绝对单一的WAR是最好的方法。 在每个环境中使用相同的JNDI名称设置资源,如果您需要检测出用于业务逻辑目的的环境,请在Tomcat启动时使用System属性。

单一构建(战争)当然是正确的方法。 但是,当涉及特定于环境的配置时,最好的方法是确保不应将所有配置.properties文件推送到所有环境。 例如,应将PROD属性文件复制到DEV或UAT。 还应避免使用Spring配置文件,因为它们会导致错综复杂的配置管理。