Java Web 应用程序配置模式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1568985/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 14:12:01  来源:igfitidea点击:

Java Web Application Configuration Patterns

javaweb-applicationsconfigurationdesign-patterns

提问by Dónal Boyle

Are there any patterns or best practices that can be used to simplify changing configuration profiles for java web applications across multiple environments. e.g. JDBC URLs, SOAP end-points, etc.

是否有任何模式或最佳实践可用于简化跨多个环境的 Java Web 应用程序的更改配置文件。例如 JDBC URL、SOAP 端点等。

As a bit of background to help clarify my question, I work with several large java web applications that during any given release cycle move through 6 different environments; development, integration, QA, performance and eventually get deployed to multiple production servers. In each environment, configuration needs to change. Right now, most configuration changes for each deployment are done manually which is both time consuming and open to errors.
Is there any way to take the manual intervention out of this process?

作为帮助澄清我的问题的一些背景知识,我使用了几个大型 Java Web 应用程序,这些应用程序在任何给定的发布周期中都会经过 6 个不同的环境;开发、集成、QA、性能并最终部署到多个生产服务器。在每个环境中,配置都需要更改。目前,每个部署的大多数配置更改都是手动完成的,这既耗时又容易出错。
有什么办法可以让人工干预摆脱这个过程吗?

采纳答案by Jeremy S

I tend to work more with .NET lately, so my Java is fairly rusty. I'm pretty sure this would work in any language with a little tweaking.

我最近更倾向于使用 .NET,所以我的 Java 相当生疏。我很确定这可以在任何语言中进行一些调整。

We use an extension of the .NET configuration system that allows us to use environment and/or application specific settings in conjunction with a more global configuration. The configuration system uses a Global setting to each machine identifies it as dev, beta, or production (the default). A set of files loaded in order, and the setting from the last file overrides any setting that was defined in a previously loaded file. Files are loaded in the following order:

我们使用 .NET 配置系统的扩展,允许我们将环境和/或应用程序特定设置与更全局的配置结合使用。配置系统使用全局设置将每台机器标识为开发、测试或生产(默认)。一组按顺序加载的文件,最后一个文件的设置会覆盖先前加载的文件中定义的任何设置。文件按以下顺序加载:

  1. Global settings
  2. Application specific settings
  3. Application specific environment overrides
  1. 全局设置
  2. 应用程序特定设置
  3. 应用程序特定环境覆盖

All the files are in source control, and since the environment is defined on the machine the application is running on; since it won't access the "beta" configuration unless the machine configuration identifies it as "beta", we can promote all of the configuration files without fear of inadvertently pointing our production application to a dev database.

所有文件都在源代码控制中,并且由于环境是在运行应用程序的机器上定义的;由于它不会访问“beta”配置,除非机器配置将其标识为“beta”,因此我们可以提升所有配置文件,而不必担心无意中将我们的生产应用程序指向开发数据库。

回答by Yishai

This is heavily going to depend on what options the web application servers give you. We have multiple environments for JBoss with different JDBC URLs, the JNDI name remains the same across all servers, just the configuration on the local instance changes, so nothing goes wrong from build to build.

这在很大程度上取决于 Web 应用程序服务器为您提供的选项。我们有多个具有不同 JDBC URL 的 JBoss 环境,JNDI 名称在所有服务器上保持不变,只是本地实例上的配置发生了变化,因此从构建到构建都没有问题。

I guess the short answer is that the best practice is to externalize the configurations and keep a good file in place with the correct settings for each server, and have the web app read that configuration. The exact nature of the externalization and reading is going to depend on the specific configuration and application server.

我想简短的回答是,最佳实践是将配置外部化,并为每个服务器保留一个具有正确设置的良好文件,并让 Web 应用程序读取该配置。外部化和读取的确切性质将取决于特定的配置和应用程序服务器。

EDIT: These configurations do not exist as part of the war (ear in our case) that way they are not overwritten.

编辑:这些配置不作为战争的一部分存在(在我们的例子中是耳朵),这样它们就不会被覆盖。

回答by kgiannakakis

At first have all the configuration settings that frequently change in one place. It is really difficult, if you need to set up JNDI, edit database values and modify property files, all at the same time, in order to complete the configuration. Prefer the medium that is easiest to edit and also easier to verify that everything is set up properly. I would say that property files is the best solution. You can easily edit them and you only need a quick look through them to see that everything is alright. If you opt for property files, carefully select a standard location for them and assign an environmental variable for the path.

首先将所有经常更改的配置设置放在一个地方。真的很难,如果你需要同时设置JNDI、编辑数据库值和修改属性文件,才能完成配置。更喜欢最容易编辑并且也更容易验证一切设置是否正确的媒体。我会说属性文件是最好的解决方案。您可以轻松地编辑它们,您只需快速浏览一下即可看到一切正常。如果您选择属性文件,请仔细为它们选择标准位置并为路径分配环境变量。

It also helps if you have a simple test that verifies that everything is set up properly. For example you can have a test page that displays the configuration parameters and performs some basic tests, like trying to connect to database or remote servers.

如果您有一个简单的测试来验证一切设置是否正确,它也会有所帮助。例如,您可以有一个测试页面,显示配置参数并执行一些基本测试,例如尝试连接到数据库或远程服务器。

回答by Eemeli Kantola

Here are some possible practices I've used or encountered. Combining these is usually needed in practice.

以下是我使用或遇到的一些可能的做法。在实践中通常需要将这些结合起来。

Substituting the variable values in conffiles when building

构建时替换conffiles中的变量值

Here's an example of how this can be done with Apache Ant. Ant properties (${var.name}) can be controlled with the build configuration files:

下面是如何使用 Apache Ant 完成此操作的示例。Ant 属性 ( ${var.name}) 可以通过构建配置文件进行控制:

<filterset id="variables.to.replace">
    <filter token="APPNAME" value="${app.name}"/>
    <filter token="WEBAPP-PATH" value="${webapp.path}"/>
    <filter token="ENCRYPT-ALGORITHM" value="${encrypt.algorithm}"/>
    <filter token="ERROR-MAILTO" value="${error.mailTo}"/>
    <!--...-->
</filterset>

<!-- Then, when building & copying the conf, replace the variables: -->
<copy todir="${properties.target.dir}">
    <!-- env specific conf files -->
    <fileset dir="${basedir}/env/${run.env}/webapp/WEB-INF/classes" />
    <filterset refid="variables.to.replace"/>
</copy>

The good thing is that you get a fine control over the different configurations at build time. What is bad is that the system tends to grow very complex and hard to maintain if you use this method extensively for a large number of different configurations. Also, having to build the conffiles, too, means slower development cycles.

好处是您可以在构建时很好地控制不同的配置。不好的是,如果您将这种方法广泛用于大量不同的配置,系统往往会变得非常复杂且难以维护。此外,必须构建配置文件也意味着更慢的开发周期。

Substituting the variables from conf inside war at webapp startup

在 webapp 启动时替换 war 中的 conf 变量

This is what I usually do when using Spring Framework, even if there is just one possble configuration, getting the benefits of the separation of concerns. With Spring, you can have the conf values replaced with PlaceholderPropertyConfigurer inside Spring context at webapp startup. In this case, you have to anyway pick the right configuration, which can be configured for example on build time.

这是我在使用 Spring Framework 时通常会做的事情,即使只有一种可能的配置,也可以获得关注点分离的好处。使用 Spring,您可以在 webapp 启动时将 conf 值替换为 Spring 上下文中的 PlaceholderPropertyConfigurer。在这种情况下,您无论如何都必须选择正确的配置,例如可以在构建时进行配置。

Compared to the build time replacing, it's easier to temporarily manipulate the values in an uncompressed webapp, if needed. Of course, the webapp needs to be rebooted if you change anything, and the manual changes won't persist across webapp redeployments. Spring is also limited to the Spring context, so this doesnt' work e.g. in web.xml(but having variables in web.xml should probably be avoided anyway because of its limitations).

与构建时替换相比,如果需要,临时操作未压缩的 web 应用程序中的值更容易。当然,如果您更改任何内容,则需要重新启动 webapp,并且手动更改不会在 webapp 重新部署中持续存在。Spring 也仅限于 Spring 上下文,因此这在 web.xml 中不起作用(但由于其局限性,无论如何应该避免在 web.xml 中使用变量)。

Reading the local conf from a pre-defined file

从预定义文件中读取本地 conf

This approach is probably the easiest one to set up: just invent a configuration file path, e.g. $HOME/mywebapp/conf.propertiesand make your webapp somehow read it at startup.

这种方法可能是最容易设置的方法:只需创建一个配置文件路径,例如$HOME/mywebapp/conf.properties,让您的 web 应用程序在启动时以某种方式读取它。

The good thing here is that you don't have to care about the conf when building/deploying the webapp. Anyway, you should have some sensible conf defaults that can then be overridden by the local conf.

这里的好处是您在构建/部署 web 应用程序时不必关心 conf。无论如何,你应该有一些合理的 conf 默认值,然后可以被本地 conf 覆盖。

Having the conf in a database

在数据库中有 conf

This is the most flexible solution for overriding conf parameters, but can also get complicated in some cases. Having the conf in a table with nameand valuecolumns should work for the most cases.

这是覆盖 conf 参数的最灵活的解决方案,但在某些情况下也会变得复杂。将 conf 放在带有namevalue列的表中应该适用于大多数情况。

Of course, you can't configure the JDBC connection urls in a database table, but this is a good solution for simple textual/numerical conf that affects the webapp's operation after the db connection has been set up. To avoid a performance penalty, make sure you cache the conf somehow if it will be frequently accessed.

当然,您不能在数据库表中配置 JDBC 连接 url,但是对于简单的文本/数字 conf,在设置 db 连接后影响 webapp 的操作,这是一个很好的解决方案。为避免性能损失,请确保以某种方式缓存 conf,如果它会被频繁访问。

Extra practices

额外的练习

As pointed out by kgiannakakis, it also helps to set up a configuration diagnosis page of some kind for your app.

正如 kgiannakakis 所指出的,它还有助于为您的应用程序设置某种类型的配置诊断页面。

回答by rsp

There are a few possible ways to approach this:

有几种可能的方法来解决这个问题:

  • use property files like you do, but add a "meta properties" file that is used to select the property file used by defining a map between an environment value (for instance localhost hostname) onto the property file name to load.

  • put your properties into a database and define the database connection to the property tables in your application server as resource that is picked up by your web-app.

  • don't put the property files in your .war or .ear, but create a properties-deployhost.jar archives containing the property files per target host. bind the appropriate .jar file to the deployed web-app by adding it to the class path (for instance via shared libraries in the application server configuration per web-app.)

  • 像您一样使用属性文件,但添加一个“元属性”文件,该文件用于选择通过定义环境值(例如 localhost 主机名)到要加载的属性文件名之间的映射来使用的属性文件。

  • 将您的属性放入数据库并将数据库连接定义为您的应用程序服务器中的属性表作为您的 Web 应用程序获取的资源。

  • 不要将属性文件放在 .war 或 .ear 中,而是创建一个 properties-deployhost.jar 存档,其中包含每个目标主机的属性文件。通过将适当的 .jar 文件添加到类路径(例如,通过每个 web 应用程序的应用程序服务器配置中的共享库)将适当的 .jar 文件绑定到部署的 web 应用程序。

Only the first of these does not need extra manual steps when deploying at the expense of having to update your config source and building new deploy files when your target systems are renamed.

在部署时,只有第一个不需要额外的手动步骤,代价是在重命名目标系统时必须更新配置源并构建新的部署文件。

I'm sure lots of variantions on these and your approach are possible, what is the best choice depends on your situation.

我相信这些和您的方法有很多变体是可能的,最佳选择取决于您的情况。

回答by MBCook

What we do works pretty well.

我们所做的工作非常有效。

On startup, our programs read a configuration file in a hardcoded path. Let's say it's:

在启动时,我们的程序读取硬编码路径中的配置文件。让我们说它是:

/config/fun_prog/config.xml

Each program has a different hard coded path (FunProgram is in fun_prog, Super Server is in sup_serv, whatever), so we don't have to worry about them walking over each other.

每个程序都有不同的硬编码路径(FunProgram 在 fun_prog 中,Super Server 在 sup_serv 中,等等),所以我们不必担心它们会互相干扰。

The XML files are read by a little configuration library we created. The XML file contains the DB connection information, usually mail server configuration data, email addresses to send notifications to, whether it should operate in test mode, URLs of external services, etc.

XML 文件由我们创建的一个小配置库读取。XML 文件包含数据库连接信息,通常是邮件服务器配置数据、发送通知的电子邮件地址、是否应在测试模式下运行、外部服务的 URL 等。

So when we need to make changes, we copy the config file, edit what we want, and restart the program. Since we have a standard server setup, any program can be deployed on any server by just copying these files around (and the neccessary httpd.conf tinkering).

因此,当我们需要进行更改时,我们复制配置文件,编辑我们想要的内容,然后重新启动程序。因为我们有一个标准的服务器设置,任何程序都可以部署在任何服务器上,只需复制这些文件(以及必要的 httpd.conf 修补)。

It's not fancy, but it works very well. It's extremely simple to understand, add new configuration options, backup, and edit. Works on all platforms (unix is obvious, Windows translates paths starting with / into c:\ so it works without edits there too).

它并不花哨,但效果很好。它非常易于理解、添加新的配置选项、备份和编辑。适用于所有平台(unix 很明显,Windows 将以 / 开头的路径转换为 ​​c:\,因此它也无需编辑即可工作)。

Our workstations basically run the same software as the server, just with a few changes in that config file.

我们的工作站基本上运行与服务器相同的软件,只是在该配置文件中进行了一些更改。

回答by cetnar

The good example what you want is used in Seamor Grails (borrowed from Rails). There are profiles, by default three: prod, dev, test, but you can define more if you want.

Seam或 Grails(从 Rails 借来的)中使用了你想要的好例子。有配置文件,默认情况下三个:prod、dev、test,但您可以根据需要定义更多。

In Seam project build is done by Ant files. Eeach file that contents can vary is defined for every profile e.g. datasource, sql scripts or properties files.

在 Seam 中,项目构建是由 Ant 文件完成的。内容可以变化的每个文件都是为每个配置文件定义的,例如数据源、sql 脚本或属性文件。

import-dev.sql
import-prod.sql
import-test.sql

import-dev.sql
import-prod.sql
import-test.sql

When ant file is run with choosen profile, appropriate file is taken and profile name is truncated from that file name.

当使用选定的配置文件运行 ant 文件时,将获取适当的文件并从该文件名中截断配置文件名。

Below is code snippet that you can place in your targets

以下是您可以放置​​在目标中的代码片段

<copy tofile="${war.dir}/WEB-INF/classes/import.sql" 
      file="${basedir}/resources/import-${profile}.sql"/>

JDBC url, driver names can be externalized to properties files (of course with profile names as suffixes)

JDBC url,驱动名称可以外化到属性文件中(当然以配置文件名作为后缀)

<filterset id="persistence">
     <filter token="transactionManagerLookupClass" value="${transactionManagerLookupClass}"/>

<copy tofile="${war.dir}/WEB-INF/classes/META-INF/persistence.xml" 
     file="${basedir}/resources/META-INF/persistence-${profile}.xml">
     <filterset refid="persistence"/>
</copy>

or values of properies you can pass to ant build call from command line. This is short example what is in Seam done.

或可以从命令行传递给 ant build 调用的属性值。这是在 Seam 中完成的简短示例。

Another option is to used Maven. In maven way it done by properties and by profiles, but you can use also separate modules to split configuration and create other modules with main functionality. Typical use case examples of maven properties and profiles are run configuration for multiple databases, deployment servers etc. It's even harder when you want to create configuration for different vendors, but for Maven that isn't a problem :)

另一种选择是使用Maven。在 maven 中,它通过属性和配置文件完成,但您也可以使用单独的模块来拆分配置并创建具有主要功能的其他模块。maven 属性和配置文件的典型用例示例是多个数据库、部署服务器等的运行配置。当您想要为不同供应商创建配置时更难,但对于 Maven 来说,这不是问题:)

Great example of using maven profiles is this post form Carlos Sanchez blog.

使用 Maven 配置文件的一个很好的例子是这篇来自Carlos Sanchez 博客的帖子。

To sum up I strongly recomend to look up Ant/Seam an Maven parametrization (profiles). Those solutions have another advantage: ant or maven script can be run in CI server (like Hudson) and let run/test simultaneously all your profiles.

总而言之,我强烈建议查找 Ant/Seam 和 Maven 参数化(配置文件)。这些解决方案还有另一个优势:ant 或 maven 脚本可以在 CI 服务器(如Hudson)中运行,并让您同时运行/测试所有配置文件。

回答by John Munsch

I'm surprised that no one cited the Jakarta Commons Configuration API (http://commons.apache.org/configuration/) to answer this question. It allows you to have a hierarchy of files (or other configuration sources like XML, JNDI, JDBC, etc.). That's what Jeremy Seghi was talking about and it gives you a good way to have both defaults and local overrides as well.

我很惊讶没有人引用 Jakarta Commons Configuration API ( http://commons.apache.org/configuration/) 来回答这个问题。它允许您拥有文件层次结构(或其他配置源,如 XML、JNDI、JDBC 等)。这就是 Jeremy Seghi 所说的,它为您提供了一种同时拥有默认值和本地覆盖的好方法。

The best part is that it is a tested working solution so you don't have to go craft something yourself.

最好的部分是它是一个经过测试的工作解决方案,因此您不必自己制作一些东西。

回答by javabegin

Please take a look at this URL: http://issues.apache.org/jira/browse/CONFIGURATION-394

请看一下这个网址:http: //issues.apache.org/jira/browse/CONFIGURATION-394

The Configuration framework which we're looking for it is something on top of Apache Commons Configuration and must support Concurrency Issues, JMX issues and most of stores(e.g .properties file, .xml files or PreferencesAPI).

我们正在寻找的配置框架位于 Apache Commons Configuration 之上,并且必须支持并发问题、JMX 问题和大多数存储(例如 .properties 文件、.xml 文件或 PreferencesAPI)。

What weblogic team provides on 'Administration Console' is intersting which through it you can have transactional(atomic) updates on configurations so that are registered listeners be notified.

weblogic 团队在“管理控制台”上提供的内容很有趣,通过它您可以对配置进行事务性(原子)更新,以便通知注册的侦听器。

The Apache guys insist that this project is out of scopes of Commons Configuration, maybe!

Apache 的家伙们坚持认为这个项目超出了 Commons Configuration 的范围,也许吧!

I've attached a simple configuration framework, take look please.

我附上了一个简单的配置框架,请看一下。

回答by Jorge DeFlon Developer

You can use the Component Configuration Pattern in your language of choice

您可以使用您选择的语言的组件配置模式

It is described in the POSA books (I think that in the 4th volume)

它在 POSA 书籍中有所描述(我认为在第 4 卷中)

(in java you can use the commons-configurationcomponent ).

(在 Java 中,您可以使用 commons-configuration组件)。