《Java核心卷 I》第10版阅读笔记第九章(书第十三章)

本文详细介绍了Java应用程序的部署技巧,包括JAR文件的创建、清单文件的使用、可执行JAR的配置以及资源的管理和访问。还讨论了应用首选项的存储,如属性映射和首选项API,以及服务加载器在插件体系结构中的作用。
摘要由CSDN通过智能技术生成


day35

第九章 部署 Java 应用程序

9.1 JAR 文件

在将应用程序进行打包时,使用者一定希望仅提供给其一个单独的文件,而不是一个含有大量类文件的目录,Java 归档(JAR) 文件就是为此目的而设计的。
一个 JAR 文件既可以包含类文件,也可以包含诸如图像和声音等其他类型的文件。此外,JAR 文件是压缩的, 它使用了大家熟悉的 ZIP 压缩格式。

9.1.1 创建 JAR 文件

●可以使用jar工具制作 JAR 文件(在默认的 JDK 安装中,位于jdk/bin 目录下)。创建一 个新的 JAR 文件应该使用的常见命令格式为:
jar cvf JARFileNameFile:File2 . . .
例如: jar cvf CalculatorClasses.jar *.class icon.gif
●可以将应用程序、 程序组件以及代码库打包在 JAR 文件中

9.1.2 清单文件

●除了类文件、 图像和其他资源外, 每个 JAR 文件还包含一个用于描述归档特征的清单文件(manifest)。 清单文件被命名为 MANIFEST.MF, 它位于JAR文件的一个特殊 META-INF子目录中。
清单文件的最后一行必须以换行符结束。 否则, 清单文件将无法被正确地读取。
●简单/复杂清单文件
最小的符合标准的清单文件是很简单的: Manifest-Version: 1.0

复杂的清单文件可能包含更多条目。这些清单条目被分成多个节。第一节被称为主节 ( main section) 。它作用于整个JAR文件。随后的条目用来指定已命名条目的属性,这些已命名的条目可以是某个文件、 包或者URL。它们都必须起始于名为 Name 的条目。节与节之间用空行分开。例如:
Manifest-Version: 1.0
描述这个归档文件的行

Name: Woozle.class
描述这个文件的行

Name: cora/mycompany/mypkg/
描述这个包的行要想编辑清单文件
●编辑清单文件
要想编辑清单文件,需要将希望添加到清单文件中的行放到文本文件中,然后运行:
jar cfm JARFileNameManifestFileName . . .

要创建一个包含清单的 JAR 文件,应该运行:
jar cfm MyArchive.jar manifest.mf com/mycompany/mypkg/*.class

要想更新一个已有的 JAR 文件的清单,则需要将增加的部分放置到一个文本文件中,然后执行下列命令: jar ufm MyArchive.jar manifest-additions.mf

9.1.3 可执行 JAR 文件

●可以使用jar命令中的 e选项指定程序的入口点, 即通常需要在调用java程序加载器时指定的类:
jar cvfe MyPrograni.jar com.mycompany.mypkg.MainAppClass files to add
或者,可以在清单中指定应用程序的主类,包括以下形式的语句:
Main-Class: com.mycompany.mypkg.MainAppClass

●不论哪一种方法,用户可以简单地通过下面命令来启动应用程序:
java -jar MyProgram.jar 。根据操作系统的配置, 用户甚至可以通过双击 JAR 文件图标来启动应用程序。

9.1.4 资源

相关的数据文件
●类加载器知道如何搜索类文件,直到在类路径、存档文件或 web 服务器上找到为止。利用资源机制, 对于非类文件也可以同样方便地进行操作。下面是必要的步骤:
1 ) 获得具有资源的 Class 对象,例如, AboutPanel.class。
2 ) 如果资源是一个图像或声音文件, 那么就需要调用 getresource (filename) 获得作为URL的资源位置,然后利用 getlmage 或 getAudioClip 方法进行读取。
3 )其他资源可以使用 getResourceAsStream 方法读取文件中的数据。
重点在于类加载器可以记住如何定位类,然后在同一位置査找关联的资源。

例如,要想利用 about.gif 图像文件制作图标,可以使用下列代码:
URL url = ResourceTest.class.getResource(“about.gif”);
Image img = new Imagelcon(url).getlmage();
这段代码的含义是“ 在找到 ResourceTest 类的地方查找 about.gif 文件”。

除了可以将资源文件与类文件放在同一个目录中外,还可以将它放在子目录中。可以使用下面所示的层级资源名: data/text/about.txt 这是一个相对的资源名,它会被解释为相对于加载这个资源的类所在的包。注意, 必须 使用“ /” 作为分隔符,而不要理睬存储资源文件的系统实际使用哪种目录分隔符。

一个以“ /” 开头的资源名被称为绝对资源名。它的定位方式与类在包中的定位方式一 样。
参考:
https://www.jb51.net/article/108654.htm
https://blog.csdn.net/wenhuiqiao/article/details/8485879

9.1.5 密封

可以将 Java 包密封 ( seal ) 以保证不会有其他的类加人到其中。
●如果在代码中使用了包可见的类、方法和域,就可能希望密封包。如果不密封, 其他类就有可能放在这个包中,进而访问包可见的特性。
●要想密封一个包,需要将包中的所有类放到一个JAR文件中。在默认情况下,JAR文件中的包是没有密封的。可以在清单文件的主节中加人下面一行:
Sealed: true
来改变全局的默认设定。对于每个单独的包,可以通过在JAR文件的清单中增加一节, 来指定是否想要密封这个包。例如:
Name: com/mycompany/util/
Sealed: true
Name: com/mycompany/misc/
Sealed: false
要想密封一个包,需要创建一个包含清单指令的文本文件。然后用常规的方式运行jar 命令:
jar cvfw MyArchive.jar manifest.mf files to add

9.2 应用首选项的存储

应用用户通常希望能保存他们的首选项和定制信息, 以后再次启动应用时再恢复这些配置。
1、Java 应用的传统做法: 将配置信息保存在属性文件中。
2、首选项 API, 提供了一个更加健壮的解决方案

9.2.1 属性映射

●属性映射(property map) 是一种存储键 / 值对的数据结构。属性映射通常用来存储配置信息,它有 3 个特性:
•键和值是字符串。
•映射可以很容易地存人文件以及从文件加载。
•有一个二级表保存默认值。
实现属性映射的 Java 类名为 Properties。 属性映射对于指定程序的配置选项很有用。例如: Properties settings = new Properties() ;
settings.setProperty(“width”, “200”);
settings.setProperty(“title”, “Hello, World!”);
●可以使用 store方法将属性映射列表保存到一个文件中。在这里, 我们将属性映射保存在文件 program.properties 中。第二个参数是包含在这个文件中的注释。
OutputStream out = new FileOutputStream(“program.properties”);
settings.store(out, “Program Properties”);
这个示例会给出以下输出:
#Progran Properties
#Mon Apr 30 07:22:52 2007
width=200
title=Hello, World!
要从文件加载属性,可以使用以下调用:
InputStream in = new FileInputStream(“program.properties”);
settings.load(in);

习惯上, 会把程序属性存储在用户主目录的一个子目录中。目录名通常以一个点号开头 (在 UNIX 系统中), 这个约定说明这是一个对用户隐藏的系统目录。
要找出用户的主目录,可以调用 System.getProperties方法,它恰好也使用一个 Properties 对象描述系统信息。主目录包含键 “user.home”。还有一个便利方法可以读取单个键:
String userDir = System.getProperty(“user.home”);
●Properties 类有两种提供默认值的机制。
第一种方法是, 查找一个字符串的值时可以指定 一个默认值,这样当键不存在时就会自动使用这个默认值。 String title = settings.getProperty(“title”, “Default title”);
第二种方法, 可以把所有默认值都放在一个二级属性映射中,并在主属性映射的构造器中提供这个二级映射。
在这里插入图片描述●出于历史上的原因,Properties 类实现了 Map<Object,Object> 。 因此, 可以使用 Map接口的 get 和 put 方法。不过,get 方法会返回类型 Object, 而 put 方法允许插入任何对象。最好坚持使用 getProperty 和 setProperty 方法,这些方法会处理字符串,而不是对象
属性映射是没有层次结构的简单表。通常会用类似 window.main.color、window,.main.title 等键名引入一个伪层次结构 , 不过 Properties 类没有提供方法来组织这样一个层次结构。如果存储复杂的配置信息, 就应当使用 Preferences 类。

9.2.2 首选项 API

●我们已经看到,利用 Properties 类可以很容易地加载和保存配置信息。不过,使用属性文件有以下缺点:
•有些操作系统没有主目录的概念, 所以很难找到一个统一的配置文件位置。
•关于配置文件的命名没有标准约定, 用户安装多个 Java 应用时,就更容易发生命名冲突。
●有些操作系统有一个存储配置信息的中心存储库,例如 Microsoft Windows 中的注册表。Preferences 类以一种平台无关的方式提供了这样一个中心存储库。在 Windows 中, Preferences 类使用注册表来存储信息;在 Linux 上, 信息则存储在本地文件系统中。当 然,存储库实现对使用 Preferences 类的程序员是透明的。
●Preferences 存储库有一个树状结构, 节点路径名类似于 /com/mycompany/myapp。类似 于包名, 只要程序员用逆置的域名作为路径的开头, 就可以避免命名冲突。实际上, API 的设计者就建议配置节点路径要与程序中的包名一致。
●存储库的各个节点分别有一个单独的键 / 值对表,可以用来存储数值、字符串或字节数组,但不能存储可串行化的对象。
●为了增加灵活性,可以有多个并行的树。每个程序用户分别有一棵树;另外还有一棵系统树, 可以用于存放所有用户的公共信息。Preferences类使用操作系统的“ 当前用户” 概念 来访问适当的用户树。 若要访问树中的一个节点,需要从用户或系统根开始:
Preferences root = Preferences.userRoot();
或 Preferences root = Preferences.systemRoot();
然后访问节点。可以直接提供一个节点路径名:
Preferences node = root.node("/com/mycompany/myapp");
如果节点的路径名等于类的包名,还有一种便捷方式来获得这个节点。只需要得到这个类的一个对象,然后调用:
Preferences node = Preferences.userNodeForPackage(obj.getClass());
或 Preferences node = Preferences.systemNodeForPackage(obj.getClass()):;
一般来说, Obj 往往是 this 引用。 一旦得到了节点,可以用以下方法访问键 / 值表:
在这里插入图片描述需要说明的是, 读取信息时必须指定一个默认值, 以防止没有可用的存储库数据。相对应地,可以用如下的 put 方法向存储库写数据:
put(String key, String value)
putInt(String key, int value)

●类似 Windows 注册表这样的中心存储库通常都存在两个问题:
•它们会变成充斥着过期信息的“ 垃圾场”。
•配置数据与存储库纠缠在一起, 以至于很难把首选项迁移到新平台。
Preferences 类为第二个问题提供了一个解决方案。可以通过调用以下方法导出一个子树 (或者比较少见的,也可以是一个节点)的首选项:
void exportSubtree(OutputStream out)
void exportNode(OutputStream out)
数据用 XML 格式保存。可以通过调用以下方法将数据导人到另一个存储库:
void importPreferences(InputStream in)

如果你的程序使用首选项, 要让用户有机会导出和导人首选项, 从而可以很容易地将设置从一台计算机迁移到另一台计算机。

9.3 服务加载器

●有时会开发采用插件体系结构的应用。 有些平台支持这种方法, 如 OSGi (http://0Sgi. org), 并用于开发环境、应用服务器和其他复杂的应用中。 JDK 还提供了一个加载插件的简单机制。
●通常, 提供一个插件时, 程序希望插件设计者能有一些自由来确定如何实现插件的特性。另外还可以有多个实现以供选择。利用 ServiceLoader 类可以很容易地加载符合一个公共接口的插件。
●定义一个接口(或者, 如果愿意也可以定义一个超类), 其中包含服务的各个实例应当提供的方法。服务提供者可以提供一个或多个实现这个服务的类。实现类可以放在任意包中, 而不一定是服务接口所在的包。每个实现类必须有一个无参 数构造器。 现在把这些类的类名增加到 META-INF/services 目录下的一个 UTF-8 编码文本文件中, 文件名必须与完全限定类名一致。

完成这个准备工作之后, 程序可以如下初始化一个服务加载器:
public static ServiceLoader< Cipher> cipherLoader = ServiceLoader.load(Cipher.class);
这个初始化工作只在程序中完成一次。

剩下的略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值