您可以像下面这样对每个服务器操作进行编码吗?
void op1(String username, ...)
{
String userScope = getConfigurationScopeForUser(username);
String language = cfg.lookupString(userScope, "language");
int fontSize = cfg.lookupInt(userScope, "font_size");
... // business logic expressed in terms of language and fontSize
}
(上面的伪代码假设用户名作为参数传递,但您可以通过另一种机制传递它,例如线程本地存储。)
如果以上可以接受,那么Config4* 可以满足您的要求。使用Config4*,上述伪代码中使用的getConfigurationScopeForUser()方法可以实现如下(这里假设cfg是一个Configuration对象,之前已经通过解析配置文件进行了初始化):
String getConfigurationScopeForUser(String username)
{
if (cfg.type("user", username) == Configuration.CFG_SCOPE) {
return Configuration.mergeNames("user", username);
} else {
return "user.default";
}
}
这是一个与上述内容一起使用的示例配置文件。大多数用户从“user.default”范围获取他们的配置,但 Mary 和 John 对其中一些默认值有自己的覆盖:
user.default {
language = "English";
font_size = "12";
# ... many other configuration settings
}
user.John {
@copyFrom "user.default";
language = "Klingon"; # override a default value
}
user.Mary {
@copyFrom "user.default";
font_size = "18"; # override a default value
}
如果上面听起来可能满足您的需求,那么我建议您阅读“入门指南”的第 2 章和第 3 章,以充分了解 Config4* 语法和 API,以便能够确认/驳斥 Config4* 是否适合您的需求。您可以在 Config4* website 上找到该文档。
免责声明:我是 Config4* 的维护者。
编辑:我正在提供更多细节以回应 bacar 的 cmets。
我没有将 Config4* 放在 Maven 存储库中。但是,使用捆绑的 Ant 构建文件构建 Config4* 是微不足道的,因为 Config4* 不 对第三方库有任何依赖。
在服务器应用程序中使用 Config4* 的另一种方法(由 bacar 的评论提示)与 Config4* 如下...
像下面的伪代码那样实现每个服务器操作:
void op1(String username, ...)
{
Configuration cfg = getConfigurationForUser(username);
String language = cfg.lookupString("settings", "language");
int fontSize = cfg.lookupInt("settings", "font_size");
... // business logic expressed in terms of language and fontSize
}
上面使用的getConfigurationForUser()方法可以实现如下伪代码所示:
HashMap<String,Configuration> map = new HashMap<String,Configuration>();
synchronized String getConfigurationForUser(String username)
{
Configuration cfg = map.get(username);
if (cfg == null) {
// Create a config object tailored for the user & add to the map
cfg = Configuration.create();
cfg.insertString("", "user", username); // in global scope
cfg.parse("/path/to/file.cfg");
map.put(username, cfg);
}
return cfg;
}
这是一个与上述内容一起使用的示例配置文件。
user ?= ""; // will be set via insertString()
settings {
@if (user @in ["John", "Sam", "Jane"]) {
language = "Klingon";
} @else {
language = "English";
}
@if (user == "Mary") {
font_size = "12";
} @else {
font_size = "10";
}
... # many other configuration settings
}
我对这两种方法的主要cmet如下:
第一种方法(一个包含大量变量和范围的Configuration 对象)可能比第二种方法(许多Configuration 对象,每个对象都包含少量变量)使用的内存略少。但我的猜测是,这两种方法的内存使用量都将以 KB 或数十 KB 为单位,与服务器应用程序的整体内存占用量相比,这将是微不足道的。
我更喜欢第一种方法,因为单个Configuration 对象只初始化一次,然后通过只读lookup() 样式操作访问它。这意味着您不必担心同步访问Configuration 对象,即使您的服务器应用程序是多线程的。相反,如果您的服务器应用程序是多线程的,则第二种方法要求您同步对HashMap 的访问。
lookup() 样式操作的开销大约是纳秒或微秒,而解析配置文件的开销大约是毫秒或几十毫秒(取决于文件的大小)。第一种方法只执行一次配置文件的相对昂贵的解析,并且在应用程序的初始化中完成。相比之下,第二种方法对配置文件执行“N”次相对昂贵的解析(对于“N”个用户中的每个用户一次),并且在服务器处理来自客户端的请求时会发生重复开销。对您的应用程序而言,这种性能下降可能是问题,也可能不是问题。
我认为易用性比易于实施更重要。所以,如果你觉得第二种方式更容易维护配置文件,那么我建议你使用那种方式。
在第二种方法中,您可能想知道为什么我将大多数变量放在命名范围 (settings) 中,而不是与“注入”user 变量一起放在全局范围中。我这样做的原因超出了您的问题范围:将“注入”变量与应用程序可见变量分开可以更轻松地对应用程序可见变量执行架构验证。