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

Posted

技术标签:

【中文标题】如何在 EAR 或 WAR 之外存储 Java EE 配置参数?【英文标题】:How can I store Java EE configuration parameters outside of an EAR or WAR? 【发布时间】:2010-11-01 04:36:59 【问题描述】:

我想在 Web 项目(ear/war 文件)之外存储 Web 项目的配置。 应用程序不应该知道它在哪个容器中运行(WebSphere/JBoss 等)。

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

JNDI 是一种干净的方式吗?如果 JNDI 可以解决我的问题,我应该如何配置它? (自定义对象?)

就我而言,SOAP/WS 端点只有简单的 Key=>Value 对(字符串、字符串)。

【问题讨论】:

Java EE 8 可能会为这样的事情引入一种称为“配置服务”的东西。见jfokus.se/jfokus13/preso/jf13_JavaEEConfiguration.pdf也见java.net/jira/browse/JAVAEE_SPEC-19 为什么不使用 jva 参数? 【参考方案1】:

请参阅此question 以读取 WAR 文件之外的属性文件。

有关从 JNDI 读取变量值的信息,请参阅此 question。我相信这是最好的解决方案。您可以使用以下代码读取字符串变量:

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 文件中定义参数的默认值。 可以在容器中轻松配置参数。 修改参数值时无需重启容器。

【讨论】:

在我的情况下,参数设置需要由“未经培训的”最终用户完成 - 导航复杂的 XML 文件以找到唯一需要更改的东西不是一种选择(太多他们冒着改变不应该改变的东西并破坏整个容器的风险。) 未经培训的最终用户在管理容器时做什么? 在许多组织中,配置是由网络管理员执行的。在某些情况下,仅允许管理员配置密码或其他敏感数据。这些人可能对 XML 没有多少经验。【参考方案2】:

在为不同的开发人员以及在 Amazon 的 EC2 上部署 web 应用程序时,我们有类似的配置要求:我们如何将配置与二进制代码分开?以我的经验,JNDI 太复杂了,并且在要使用的容器之间变化太大。此外,手工编辑 XML 很容易受到语法错误的影响,所以这个想法被抛弃了。我们通过基于一些规则的设计解决了这个问题:

1) 只能使用简单的 name=value 条目

2) 只需更改一个参数即可加载新配置

3) 我们的 WAR 二进制文件必须是可重新配置的,无需重新打包

4) 敏感参数(密码)永远不会被打包到二进制文件中

使用 .properties 文件进行所有配置,并使用System.getProperty("domain"); 加载相应的属性文件,我们能够满足要求。但是,系统属性并不指向文件 URL,而是我们创建了一个称为“域”的概念来指定要使用的配置。配置位置始终为:$HOME/appName/config/$DOMAIN.properties

因此,如果我想使用自己的配置运行我的应用程序,我会通过将域设置为我的名字来启动应用程序:-Ddomain=jason 在启动时,应用程序会加载文件:/home/jason/appName/config/jason.properties 这使开发人员可以共享配置,以便我们可以重新创建应用程序的相同状态以进行测试和部署,而无需重新编译或重新打包。然后使用域值从捆绑的 WAR 之外的标准位置加载 .properties。

我可以使用生产配置在我的工作站上完全重新创建生产环境,例如:-Ddomain=ec2 这将加载:/home/jason/appName/config/ec2.properties

此设置允许我们在每个环境中使用不同的配置,使用一组完全编译的二进制文件进行开发/QA/发布周期。将密码/等捆绑在二进制文件中没有风险,人们可以分享他们的配置来重现我们看到的问题。

【讨论】:

嗨 Martin,是的,它确实适用于不同的应用程序服务器,因为对应用程序的依赖性为零。我们将它用于 Tomcat 和 Jetty,它们的行为方式相同。它也应该适用于任何非网络应用程序。 不错。但是,我相信某些应用服务器可能会限制您对文件系统的访问,然后这对它们不起作用。 JNDI 是要走的路。解耦配置和战争【参考方案3】:

我使用环境变量指向一个包含我的配置的 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");
   




【讨论】:

虽然简单,但缺点是需要更改值需要重启容器。 如果您需要更改文件的位置,您只需重新启动容器 - 您可以在需要重新读取文件内容的任何时候执行 loadConfiguration()。【参考方案4】:

您可以只存储类路径上的普通 java 属性文件并加载属性吗?

这很简单也很简单.. 除非我遗漏了什么

【讨论】:

虽然这听起来太简单了,但它确实是一个不错的选择。许多应用服务器都有添加到类路径的路径,因此您可以将 .properties 文件放在那里。 很多应用程序从 jndi 切换到这种替代方式,将 /etc 添加到类路径【参考方案5】:

我最喜欢的地方是:环境变量和属性文件(正如上面的 Jared 和 kgiannakakis 所建议的那样。)

数据库表存储环境属性

然而,另一种更简单的解决方案是让数据库表存储环境属性。

如果您的应用程序使用数据库

这相对容易设置 提供非常简单的方法来控制/更改值 通过使其成为数据库脚本的一部分,可以很好地集成到流程中

【讨论】:

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

以上是关于如何在 EAR 或 WAR 之外存储 Java EE 配置参数?的主要内容,如果未能解决你的问题,请参考以下文章

jar/war/ear文件的区别

java的war、jar、ear包的作用分别是啥?

如何使用 Maven 在 EAR 中添加 WAR

我无法在 JBOSS EAP 7.1 中从 EAR 或 WAR 设置系统属性

Java的JAR包, EAR包 ,WAR包 都是干啥的,有啥区别

Java中的JAR/EAR/WAR包的文件夹结构说明(转)