如何在 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 配置参数?的主要内容,如果未能解决你的问题,请参考以下文章
我无法在 JBOSS EAP 7.1 中从 EAR 或 WAR 设置系统属性