应用程序运行在平台环境(platform environment)中,平台环境由底层操作系统、Java虚拟机、类库和启动应用程序时提供的各种配置数据定义。本章介绍一些用于检查和配置平台环境的API和应用程序。
14.1 配置工具
本节介绍一些配置工具,它们帮助应用程序访问其启动环境。
14.1.1 属性
属性(property)是作为键/值对(key/value pair)管理的配置值。在每个对中,键和值都是String值。键标识符用于获得值,它很像用于获得变量值的变量名称。例如,能够下载文件的应用程序可能使用一个名为“download.lastDirectory”的属性跟踪用于最近下载的目录。
为了管理属性,需要创建java.util.Properties的实例。这个类提供用于如下目的的方法:
l 从流把键/值对加载到Properties对象中。
l 从其键获得值。
l 列出键和它们的值。
l 对键进行枚举。
l 把属性保存到流。
关于流的介绍请参考10.1节。
Properties扩展了java.util.Hashtable。从Hashtable继承的一些方法支持如下操作:
l 检查特定的键或者值是否在Properties对象中。
l 获得当前的键/值对数量。
l 删除一个键及其值。
l 把键/值对添加到Properties清单中。
l 对值或者键进行枚举。
l 通过其键获得一个值。
l 查看Properties对象是否为空。
安全考虑 访问属性涉及由当前安全管理器批准的问题。这一小节中的示例代码片段假定是独立应用程序中的代码,默认情况下,这样的应用程序没有安全管理器。根据applet运行所处的浏览器的不同,applet中的相同代码可能无法工作。关于applet的安全限制的信息,请参考docs/books/tutorial/deployment/applet/proper- ties.html。
System类维护定义当前工作环境的配置的Properties对象。更多关于属性的介绍,参见14.2.2节。本节的剩余部分将会讲解如何使用属性管理应用程序配置。
1. 应用程序生存周期中的属性
图14-1演示典型的应用程序在其执行过程中将如何使用Properties对象管理其配置数据。
|
|
|
|
|
|
|
|
图14-1 属性角色
l 启动——当应用程序启动时,进行前三个方框中描述的操作。首先,应用程序从众所周知的位置把默认属性加载到Properties对象中。通常,默认属性和应用程序的.class以及其他源文件一起存储在磁盘上的文件中。
接下来,应用程序创建另一个Properties对象,并且加载上次应用程序运行时保存的属性。很多应用程序在每个用户的基础上存储属性,所以这个步骤中加载的属性通常在特定目录的指定文件中,由这个应用程序在这个用户的主目录中维护这个特定目录。最后,应用程序使用默认或者存储的属性初始化它自己。
这里的关键在于一致性。应用程序加载和保存属性所使用的位置必须是一致的,以便它可以在下次执行时找到属性。
l 运行——在应用程序的执行期间,用户可能在Preferences窗口中改动某些设置,并且更新Properties对象反映这些改动。如果用户的改动要在未来的会话中恢复,就必须存储它们。
l 退出——在退出时,应用程序把属性保存到众所周知的位置,以便在下次启动应用程序时再次加载它们。
2. 设置Properties对象
下面的Java代码执行前一小节所描述的前两个步骤——加载默认属性和加载存储的属性:
...
// create and load default properties
Properties defaultProps = new Properties();
FileInputStream in = new FileInputStream("defaultProperties");
defaultProps.load(in);
in.close();
// create application properties with default
Properties applicationProps = new Properties(defaultProps);
// now load properties from last invocation
in = new FileInputStream("appProperties");
applicationProps.load(in);
in.close();
...
首先,应用程序设置默认的Properties对象。如果其他位置没有显式地设置值,那么就使用这个对象包含的属性集合。然后load方法从磁盘上名为defaultProperties的文件读取默认值。
接下来,应用程序使用不同的构造器创建第二个Properties对象applicationProps,它的默认值包含在defaultProps中。当获得属性时默认值开始起作用。如果在applica- tionProps中找不到属性,那么就搜索默认清单。
最后,代码从名为appProperties的文件把属性集合加载到applicationProps中。这个文件中的属性是上次调用应用程序时保存的那些属性,如下一小节所述。
3. 保存属性
下面的示例使用Properties.store写出前一个示例的应用程序属性。不需要保存默认属性,因为它们从不会改变:
FileOutputStream out = new FileOutputStream("appProperties");
applicationProps.store(out, "---No Comment---");
out.close();
store方法需要一个流去写入它,以及一个字符串作为输出开头的注释。
4. 获得属性信息
应用程序设置了其Properties对象之后,应用程序就可以查询这个对象,获得它包含的关于各种键和值的信息。应用程序启动后从Properties对象获得信息,以便能够根据用户做出的选择初始化它自己。Properties类有若干用于获得属性信息的方法:
contains(Object value)
containsKey(Object key)
如果Properties对象中包含指定的值或者键,则返回true。Properties从Hashtable继承这些方法。因此它们接受Object实参,但是只使用String值。
getProperty(String key)
getProperty(String key, String default)
返回指定属性的值。第二个版本提供默认值。如果没有找到这个键,则返回默认值。
list(PrintStream s)
list(PrintWriter w)
把所有属性写入到指定的流或者写入器。这对调试很有帮助。
elements()
keys()
propertyNames()
返回包含Properties对象中的键或者值的Enumeration(就像方法名称表述的)。keys方法只返回对象本身的键;propertyNames方法还返回默认属性的键。
stringPropertyNames()
和propertyNames类似,但是返回Set<String>,并且只返回键和值都是字符串的那些属性的名称。注意,这个Set对象和Properties对象没有关联,所以一个对象中的改动不会影响另一个对象。
size()
返回键/值对的当前数量。
5. 设置属性
在应用程序的执行期间,用户和应用程序的交互可能影响属性设置。这些改动反映在Properties对象中,以便应用程序退出(并且调用store方法)时保存它们。下面的方法改变Properties对象中的属性:
setProperty(String key, String value)
把键/值对存入Properties对象中。
remove(Object key)
删除和键相关联的键/值对。
注意 刚才介绍的一些方法是在Hashtable中定义的,因此接受String类型之外的键和值参数。即使方法允许使用其他类型,但是总要使用String类型的键和值。另外,不要对Properties对象调用Hashtable.set或者Hastable.setAll;而使用Properties. setProperty。
14.1.2 命令行参数
Java应用程序可以从命令行接受任意数量的参数。这就允许用户在启动应用程序时指定配置信息。
用户在调用应用程序时输入命令行参数,并且在要运行的类名称后面指定实参。例如,假设有一个名为Sort的Java应用程序,它对文件中的行进行排序。为了对名为friends.txt的文件中的数据进行排序,用户可以输入:
java Sort friends.txt
应用程序运行时系统通过一个String数组把命令行参数传递给应用程序的main方法。在前面的例子中,传递给Sort应用程序的命令行参数放在一个数组中,数组包含单一String:“friends.txt”。
1. 输出命令行参数
Echo示例显示其命令行参数,一个实参一行:
public class Echo {
public static void main (String[] args) {
for (String s: args) {
System.out.println(s);
}
}
}
下面的例子显示用户可能如何运行Echo。用户的输入用斜体显示:
java Echo Drink Hot Java
Drink
Hot
Java
注意,应用程序在单独的一行显示每个单词——Drink、Hot和Java。这是因为空格字符分开了命令行参数。为了使Drink、Hot和Java被解释为单一实参,用户可以通过把它们括在引号中把它们连接起来:
java Echo "Drink Hot Java"
Drink Hot Java
2. 解析数字命令行参数
如果需要应用程序支持命令行数字参数,它就必须把表示数字的String(比如“34”)转换为数字值。下面的代码片段把命令行参数转换为int:
int firstArg;
if (args.length > 0) {
try {
firstArg = Integer.parseInt(args[0]);
} catch (NumberFormatException e) {
System.err.println("Argument must be an integer");
System.exit(1);
}
}
如果args[0]的格式非法,parseInt就会抛出NumberFormatException异常。Number类(Integer、Float、Double等)具有parseXXX方法,它把表示数字的String转换为数字类型的对象。
14.1.3 环境变量
很多操作系统使用环境变量(environment variable)把配置信息传递给应用程序。和Java平台中的属性一样,环境变量是键/值对,键和值都是字符串值。因为各种操作系统之间的差异,以及各种命令行解释器之间的差异,设置和使用环境变量的约定各不相同。为了了解操作系统如何把环境变量传递给应用程序,请参考系统文档。
1. 查询环境变量
在Java平台上,应用程序使用System.getEnv获得环境变量值。不带实参的getEnv返回只读的java.util.Map实例,其中映射键是环境变量名称,而映射值是环境变量值。EnvMap示例中演示这种情况:
import java.util.Map;
public class EnvMap {
public static void main (String[] args) {
Map<String, String> env = System.getenv();
for (String envName : env.keySet()) {
System.out.format("%s=%s%n", envName, env.get(envName));
}
}
}
带有String实参的getEnv返回指定变量的值。如果这个变量没有定义,getEnv则返回null。Env示例通过这种方式使用getEnv查询在命令行中指定的特定环境变量:
public class Env {
public static void main (String[] args) {
for (String env: args) {
String value = System.getenv(env);
if (value != null) {
System.out.format("%s=%s%n", env, value);
} else {
System.out.format("%s is not assigned.%n", env);
}
}
}
}
2. 把环境变量传递给新进程
当Java应用程序使用ProcessBuilder对象创建新进程时,传递给新进程的环境变量的默认集合和提供给应用程序的虚拟机进程的集合相同。应用程序使用ProcessBuilder. environment改变这个集合。
3. 平台相关性问题
在不同系统实现环境变量的方式之间有很多微妙区别。例如,Windows忽略环境变量值中的大小写,而UNIX则不同。使用环境变量的方式也各不相同。例如Windows在名为USERNAME的环境变量中提供用户名,而UNIX实现可能通过USER、LOGNAME或者二者结合的方式提供用户名。
为了使可移植性最大化,如果在系统属性中提供了相同的值,就不要引用环境变量。例如,如果操作系统提供用户名,它总在系统属性user.name中可用。
14.1.4 其他配置工具
这里总结一些其他的配置工具。
首选项API(Preferences API)允许应用程序在和实现相关的存储中存储和获得配置数据。支持异步更新,并且相同的首选项集合可以被多线程(甚至多个应用程序)安全地更新。关于更多信息,请参阅首选项API指南(Preferences API Guide)。
部署在JAR档案文件(JAR archive)中的应用程序使用清单(manifest)描述文件的内容。关于更多信息,请参阅第16章。
Java Web Start应用程序的配置包含在JNLP文件中。关于更多信息,请参阅第17章。
Java Plug-in applet的一部分配置由用于把applet嵌入Web页面的HTML标记确定。根据applet和浏览器的不同,这些标记可以包括<applet>、<object>、<embed>和<param>。关于更多信息,请参阅第18章。
java.util.ServiceLoader提供简单的服务提供器(service provider)工具。服务提供器是服务(service)——众所周知的接口和(通常是抽象的)类的集合的实现。服务提供器中的类通常实现服务中定义的接口并且子类化服务中定义的类。服务提供器可以作为扩展安装(参见docs/books/tutorial/ext/index.html)。也可以通过把提供器添加到类路径或者通过平台特定的一些其他方式,使提供器可用。






