如何将Java EE配置参数存储在EAR或WAR之外?

37

我想将Web项目的配置保存在Web项目之外的地方(ear / war文件)。应用程序不应知道它正在运行的容器(如WebSphere / JBoss等)。

处理这个问题的最佳方法是什么?

JNDI是否是一种清洁的方法?如果JNDI可以解决我的问题,那么我应该如何配置它?(自定义对象?)

在我的情况下,只有简单的键值对(String,String),用于SOAP / WS端点。


2
Java EE 8 可能会引入一种称为“配置服务”的东西,用于处理此类事情。请参见 http://www.jfokus.se/jfokus13/preso/jf13_JavaEEConfiguration.pdf,也可以参见 http://java.net/jira/browse/JAVAEE_SPEC-19。 - Arjan Tijms
为什么不使用Java参数? - djmj
5个回答

27

查看此问题,以读取WAR文件外的属性文件。

查看此问题,以从JNDI中读取变量值。我认为这是最佳解决方案。您可以使用以下代码读取字符串变量:

Context initialContext = new InitialContext();
String myvar = (String) initialContext.lookup("java:comp/env/myvar");

以上代码适用于所有容器。在Tomcat中,您需要在conf/server.xml文件中声明以下内容:

<GlobalNamingResources ...>
  <Environment name="myvar" value="..."
         type="java.lang.String" override="false"/>
</GlobalNamingResources>

以上内容将创建一个全局资源。也可以在应用程序的上下文中定义资源。在大多数容器中,JNDI资源可以通过MBeans管理控制台访问。其中一些提供了图形界面来编辑它们。当进行更改时,最多需要重新启动应用程序。

JNDI资源的定义和编辑是容器特定的。这是配置员/管理员应用适当设置的工作。

JNDI提供的好处包括:

  • 您可以在WAR/EAR文件中定义参数的默认值。
  • 容器中的参数很容易配置。
  • 修改参数值时无需重启容器。

2
在我的情况下,需要由“未经培训”的最终用户进行参数设置 - 浏览复杂的XML文件以查找唯一需要更改的内容不是一个选项(太多风险他们会更改不应该更改的东西并破坏整个容器)。 - Jared
一个未经培训的终端用户在管理容器做什么? - Draemon
4
在许多组织中,配置工作由网络管理员负责。在某些情况下,仅允许经理配置密码或其他敏感数据。这些人可能对XML缺乏经验。 - kgiannakakis

11
我们在为不同的开发人员部署Web应用程序时,在Amazon的EC2上有一个类似的配置要求:如何将配置与二进制代码分离?根据我的经验,JNDI过于复杂,并且在容器之间变化太大,不能使用。同时,手动编辑XML非常容易出现语法错误,因此这个想法被放弃了。我们基于以下几个规则设计解决方案:
1)只使用简单的名称=值条目
2)只需更改一个参数即可加载新的配置
3)我们的WAR二进制文件必须可以重新配置而无需重新打包
4)敏感参数(密码)永远不会打包在二进制文件中
通过使用.properties文件进行所有配置,并使用System.getProperty("domain");来加载适当的属性文件,我们能够满足要求。但是,系统属性并没有指向文件URL,相反,我们创建了一个称为“domain”的概念来指定要使用的配置。配置的位置始终为:
$HOME/appName/config/$DOMAIN.properties
因此,如果我想使用自己的配置运行我的应用程序,我可以通过在启动时设置域名来启动应用程序:
-Ddomain=jason
然后应用程序会加载文件:
/home/jason/appName/config/jason.properties
这使得开发人员可以共享配置,以便我们可以在不重新编译或重新打包的情况下重新创建应用程序的相同状态进行测试和部署。然后使用域值从标准位置加载.properties文件,而不是捆绑的WAR之外。
我可以通过使用生产配置来完全在我的工作站上重新创建生产环境,例如:
-Ddomain=ec2, 这将加载:
/home/jason/appName/config/ec2.properties 这种设置允许我们在每个环境中使用不同的配置,使用完全相同的编译二进制文件进行开发/ QA /发布周期。没有密码等信息捆绑在二进制文件中的风险,并且人们可以共享他们的配置以重新创建我们正在看到的问题。

嗨,马丁,是的,它适用于不同的应用服务器,因为它与应用程序没有任何依赖关系。我们正在使用它来处理Tomcat和Jetty,它们的行为方式相同。它也应该适用于任何非Web应用程序。 - Jason Thrasher
不错。然而,我相信一些应用服务器可能会限制您对文件系统的访问,因此这对它们来说可能行不通。 - Thorbjørn Ravn Andersen
1
JNDI是正确的选择。解耦配置和war。 - ramon_salla

10
我使用环境变量来指向一个URL(可能是file:// URL),其中包含了我的配置信息。这非常简单易行,无需JNDI基础设施。
下面是一些示例代码(根据记忆打的 - 我没有编译/测试过):
public void loadConfiguration() {
   String configUrlStr = System.getenv("CONFIG_URL"); // You'd want to use a more
                                                      // Specific variable name.
   if(configUrlStr == null || configUrlStr.equals("") {
       // You would probably want better exception handling, too.
       throw new RuntimeException("CONFIG_URL is not set in the environment."); 
   }


   try {
       URI uri = new URI(configUrlStr);
       File configFile = new File(uri);
       if(!configFile.exists()) {
          throw new RuntimeException("CONFIG_URL points to non-existant file");
       }
       if(!configFile.canRead()) {
          throw new RuntimeException("CONFIG_URL points to a file that cannot be read.");
       }
       this.readConfiguration(configFile);
   } catch (URISyntaxException e) {
       throw new RuntimeException("Malformed URL/URI in CONFIG_URL");
   }



}

虽然它很简单,但缺点是如果需要更改值,则需要重新启动容器。 - kgiannakakis
1
您只需在需要更改文件位置时重新启动容器 - 您可以随时执行loadConfiguration()以重新读取文件内容。 - Jared

9
你可以将它们存储在类路径上的普通Java属性文件中,然后只需加载这些属性即可。这很简单明了...除非我漏掉了什么东西。

2
尽管听起来过于简单,但这真的是一个很好的选择。许多应用服务器都有添加到类路径的路径,因此您可以在那里放置 .properties 文件。 - alex
很多应用程序从JNDI切换到这种替代方式,将/ etc添加到类路径中。 - Martin K.

4
我最喜欢的地方是:环境变量和属性文件(正如上面Jared和kgiannakakis所建议的)。 存储环境属性的数据库表 然而,另一个更简单的解决方案是有一个存储环境属性的数据库表。
如果您的应用程序使用数据库,则:
  • 这相对容易设置
  • 提供了一种非常简单的方法来控制/更改值
  • 可以通过将其作为DB脚本的一部分来很好地集成到过程中。

2
如果您已经在使用数据库,那么将配置参数存储在表中是最简单的解决方案。由于您已经拥有生产/开发/测试数据库,因此可以轻松地为不同的部署存储不同的值。当进行更改时,甚至无需重新启动应用程序。 - kgiannakakis
2
但是,你如何获取连接到数据库所需的连接信息(这正是我希望从这个解决方案中能够获得的信息)?这是一个先有鸡还是先有蛋的问题。 - Jared
1
同意 - 杰瑞德。我会说对于数据库,您可以依赖于具有相同JNDI字符串的JNDI。因此,您的应用程序将始终指向java:comp / db / datasource。有关DB的配置信息(URL、用户、密码等)存储在容器外部。DB属性可用于诸如URL、外部字符串、环境相关常量值等的内容。某些容器提供将所有这些指定为JNDI,但并非所有容器都提供(例如,WebSphere允许将字符串值与JNDI关联,据我所知,Weblogic不允许)。 - Kartik Shah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接