1. 对于大多数 Java 开发人员来说,JAR 文件及其 “近亲” WAR 和 EAR 都只不过是漫长的 Ant 或 Maven 流程的最终结果。标准步骤是将一个 JAR 复制到服务器(或者,少数情况下是用户机)中的合适位置,然后忘记它。
  2. 事实上,JAR 能做的不止是存储源代码,您应该了解 JAR 还能做什么,以及如何进行。在这一期的 5 件事 系列中,将向您展示如何最大限度地利用 Java Archive 文件(有时候也可是 WAR 和 EAR),特别是在部署时。
  3. 由于有很多 Java 开发人员使用 Spring(因为 Spring 框架给传统的 JAR 使用带来一些特有的挑战),这里有几个具体技巧用于在 Spring 应用程序中处理 JAR 。
  4. 我将以一个标准 Java Archive 文件产生过程的简单示例开始,这将作为以下技巧的基础。
  5. 把它放在 JAR 中
  6. 通常,在源代码被编译之后,您需要构建一个 JAR 文件,使用 jar 命令行实用工具,或者,更常用的是 Ant jar 任务将 Java 代码(已经被包分离)收集到一个单独的集合中,过程简洁易懂,我不想在这做过多的说明,稍后将继续说明如何构建 JAR。现在,我只需要存档 Hello,这是一个独立控制台实用工具,对于执行打印消息到控制台这个任务十分有用。如例 1 所示:
  7. 例 1. 存档控制台实用工具
  8. package com.tedneward.jars;
  9. public class Hello
  10. {
  11. public static void main(String[] args)
  12. {
  13. System.out.println("Howdy!");
  14. }
  15. }
  16. Hello 实用工具内容并不多,但是对于研究 JAR 文件却是一个很有用的 “脚手架”,我们先从执行此代码开始。
  17. 回页首
  18. 1. JAR 是可执行的
  19. .NET 和 C++ 这类语言一直是 OS 友好的,只需要在命令行(helloWorld.exe)引用其名称,或在 GUI shell 中双击它的图标就可以启动应用程序。然而在 Java 编程中,启动器程序 — java — 将 JVM 引导入进程中,我们需要传递一个命令行参数(com.tedneward.Hello)指定想要启动的 main() 方法的类。
  20. 这些附加步骤使使用 Java 创建界面友好的应用程序更加困难。不仅终端用户需要在命令行输入所有参数(终端用户宁愿避开),而且极有可能使他或她操作失误以及返回一个难以理解的错误。
  21. 这个解决方案使 JAR 文件 “可执行” ,以致 Java 启动程序在执行 JAR 文件时,自动识别哪个类将要启动。我们所要做的是,将一个入口引入 JAR 文件例文件(MANIFEST.MF 在 JAR 的 META-INF 子目录下),像这样:
  22. 例 2. 展示入口点!
  23. Main-Class: com.tedneward.jars.Hello
  24. 这个例文件只是一个名值对。因为有时候例文件很难处理回车和空格,然而在构建 JAR 时,使用 Ant 来生成例文件是很容易的。在例 3 中,使用 Ant jar 任务的 manifest 元素来指定例文件:
  25. 例 3. 构建我的入口点!
  26. <target name="jar" depends="build">
  27. <jar destfile="outapp.jar" basedir="classes">
  28. <manifest>
  29. <attribute name="Main-Class" value="com.tedneward.jars.Hello" />
  30. </manifest>
  31. </jar>
  32. </target>
  33. 现在用户在执行 JAR 文件时需要做的就是通过 java -jar outapp.jar 在命令行上指定其文件名。就 GUI shell 来说,双击 JAR 文件即可。
  34. 回页首
  35. 2. JAR 可以包括依赖关系信息
  36. 似乎 Hello 实用工具已经展开,改变实现的需求已经出现。Spring 或 Guice 这类依赖项注入(DI)容器可以为我们处理许多细节,但是仍然有点小问题:修改代码使其含有 DI 容器的用法可能导致例 4 所示的结果,如:
  37. 例 4. Hello、Spring world!
  38. package com.tedneward.jars;
  39. import org.springframework.context.*;
  40. import org.springframework.context.support.*;
  41. public class Hello
  42. {
  43. public static void main(String[] args)
  44. {
  45. ApplicationContext appContext =
  46. new FileSystemXmlApplicationContext("./app.xml");
  47. ISpeak speaker = (ISpeak) appContext.getBean("speaker");
  48. System.out.println(speaker.sayHello());
  49. }
  50. }
  51. 关于 Spring 的更多信息
  52. 这个技巧将帮助您熟悉依赖项注入和 Spring 框架。
  53. 由于启动程序的 -jar 选项将覆盖 -classpath 命令行选项中的所有内容,因此运行这些代码时,Spring 必须是在 CLASSPATH 和 环境变量中。幸运的是,JAR 允许在例文件中出现其他的 JAR 依赖项声明,这使得无需声明就可以隐式创建 CLASSPATH,如例 5 所示:
  54. 例 5. Hello、Spring CLASSPATH!
  55. <target name="jar" depends="build">
  56. <jar destfile="outapp.jar" basedir="classes">
  57. <manifest>
  58. <attribute name="Main-Class" value="com.tedneward.jars.Hello" />
  59. <attribute name="Class-Path"
  60. value="./lib/org.springframework.context-3.0.1.RELEASE-A.jar
  61. ./lib/org.springframework.core-3.0.1.RELEASE-A.jar
  62. ./lib/org.springframework.asm-3.0.1.RELEASE-A.jar
  63. ./lib/org.springframework.beans-3.0.1.RELEASE-A.jar
  64. ./lib/org.springframework.expression-3.0.1.RELEASE-A.jar
  65. ./lib/commons-logging-1.0.4.jar" />
  66. </manifest>
  67. </jar>
  68. </target>
  69. 注意 Class-Path 属性包含一个与应用程序所依赖的 JAR 文件相关的引用。您可以将它写成一个绝对引用或者完全没有前缀。这种情况下,我们假设 JAR 文件同应用程序 JAR 在同一个目录下。
  70. 不幸的是,value 属性和 Ant Class-Path 属性必须出现在同一行,因为 JAR 例文件不能处理多个 Class-Path 属性。因此,所有这些依赖项在例文件中必须出现在一行。当然,这很难看,但为了使 java -jar outapp.jar 可用,还是值得的!
  71. 回页首
  72. 3. JAR 可以被隐式引用
  73. 如果有几个不同的命令行实用工具(或其他的应用程序)在使用 Spring 框架,可能更容易将 Spring JAR 文件放在公共位置,使所有实用工具能够引用。这样就避免了文件系统中到处都有 JAR 副本。Java 运行时 JAR 的公共位置,众所周知是 “扩展目录” ,默认位于 lib/ext 子目录,在 JRE 的安装位置之下。
  74. JRE 是一个可定制的位置,但是在一个给定的 Java 环境中很少定制,以至于可以完全假设 lib/ext 是存储 JAR 的一个安全地方,以及它们将隐式地用于 Java 环境的 CLASSPATH 上。
  75. 回页首
  76. 4. Java 6 允许类路径通配符
  77. 为了避免庞大的 CLASSPATH 环境变量(Java 开发人员几年前就应该抛弃的)和/或命令行 -classpath 参数,Java 6 引入了类路径通配符 的概念。与其不得不启动参数中明确列出的每个 JAR 文件,还不如自己指定 lib/*,让所有 JAR 文件列在该目录下(不递归),在类路径中。
  78. 不幸的是,类路径通配符不适用于之前提到的 Class-Path 属性例入口。但是这使得它更容易启动 Java 应用程序(包括服务器)开发人员任务,例如 code-gen 工具或分析工具。
  79. 回页首
  80. 5. JAR 有的不只是代码
  81. Spring,就像许多 Java 生态系统一样,依赖于一个描述构建环境的配置文件,前面提到过,Spring 依赖于一个 app.xml 文件,此文件同 JAR 文件位于同一目录 — 但是开发人员在复制 JAR 文件的同时忘记复制配置文件,这太常见了!
  82. 一些配置文件可用 sysadmin 进行编辑,但是其中很大一部分(例如 Hibernate 映射)都位于 sysadmin 域之外,这将导致部署漏洞。一个合理的解决方案是将配置文件和代码封装在一起 — 这是可行的,因为 JAR 从根本上来说就是一个 “乔装的” ZIP 文件。 当构建一个 JAR 时,只需要在 Ant 任务或 jar 命令行包括一个配置文件即可。
  83. JAR 也可以包含其他类型的文件,不仅仅是配置文件。例如,如果我的 SpeakEnglish 部件要访问一个属性文件,我可以进行如下设置,如例 6 所示:
  84. 例 6. 随机响应
  85. package com.tedneward.jars;
  86. import java.util.*;
  87. public class SpeakEnglish
  88. implements ISpeak
  89. {
  90. Properties responses = new Properties();
  91. Random random = new Random();
  92. public String sayHello()
  93. {
  94. // Pick a response at random
  95. int which = random.nextInt(5);
  96. return responses.getProperty("response." + which);
  97. }
  98. }
  99. 可以将 responses.properties 放入 JAR 文件,这意味着部署 JAR 文件时至少可以少考虑一个文件。这只需要在 JAR 步骤中包含 responses.properties 文件即可。
  100. 当您在 JAR 中存储属性之后,您可能想知道如何将它取回。如果所需要的数据与 JAR 文件在同一位置,正如前面的例子中提到的那样,不需要费心找出 JAR 文件的位置,使用 JarFile 对象就可将其打开。相反,可以使用类的 ClassLoader 找到它,像在 JAR 文件中寻找 “资源” 那样,使用 ClassLoader getResourceAsStream() 方法,如例 7 所示:
  101. 例 7. ClassLoader 定位资源
  102. package com.tedneward.jars;
  103. import java.util.*;
  104. public class SpeakEnglish
  105. implements ISpeak
  106. {
  107. Properties responses = new Properties();
  108. // ...
  109. public SpeakEnglish()
  110. {
  111. try
  112. {
  113. ClassLoader myCL = SpeakEnglish.class.getClassLoader();
  114. responses.load(
  115. myCL.getResourceAsStream(
  116. "com/tedneward/jars/responses.properties"));
  117. }
  118. catch (Exception x)
  119. {
  120. x.printStackTrace();
  121. }
  122. }
  123. // ...
  124. }
  125. 您可以按照以上步骤寻找任何类型的资源:配置文件、审计文件、图形文件,等等。几乎任何文件类型都能被捆绑进 JAR 中,作为一个 InputStream 获取(通过 ClassLoader),并通过您喜欢的方式使用。
  126. 注意,所有的 JAR 相关技巧对于 WAR 同样可用,一些技巧(特别是 Class-Path 和 Main-Class 属性)对于 WAR 来说不是那么出色,因为 servlet 环境需要全部目录,并且要有一个预先确定的入口点,但是,总体上来看这些技巧可以使我们摆脱 “好的,开始在该目录下复制......” 的模式,这也使得他们部署 Java 应用程序更为简单。

理解jar的更多相关文章

  1. JAR、WAR、EAR的使用和区别

    最近接触这几个词较多,停下来总结总结它们的区别和联系,更好的深刻理解 Jar.war.EAR.在文件结构上,三者并没有什么不同,它们都采用zip或jar档案文件压缩格式.但是它们的使用目的有所区别: ...

  2. myeclipse 反编译插件 jad 安装

    1.  准备工作 下载jad.exe文件:http://www.varaneckas.com/sites/default/files/jad/jad158g.win.zip 下载jadeclipse插 ...

  3. Maven、gradle、Ant、Eclipse IDE

    Maven.gradle.Ant.Eclipse IDE之间的关系 http://wenku.baidu.com/view/d33208810912a21615792910.html?from=sea ...

  4. Spring Boot 各Starter介绍

    原文链接:https://blog.csdn.net/u014430366/article/details/53648139 Spring-Boot-Starters 最通俗的理解- jar 包,引用 ...

  5. SpringBoot集成log4j,解决log4j.properties不生效问题

    Spring Boot集成log4j其实比较简单,maven的话,在xml中增加log4j依赖就行 <dependency> <groupId>org.springframew ...

  6. 导入项目后下载jar包问题理解

    1.导入,然后你下载的jar包,jar下载成功,并不代表你项目里的代码就能用这个jar包了. 你必须还要本地添加进classpath.必须手动添加进类路径 2.总结就是:下载jar包是下载jar包,添 ...

  7. 《深入理解Java虚拟机》虚拟机性能监控与故障处理工具

    上节学习回顾 从课本章节划分,<垃圾收集器>和<内存分配策略>这两篇随笔同属一章节,主要是从理论+实验的手段来讲解JVM的内存处理机制.好让我们对JVM运行机制有一个良好的概念 ...

  8. java笔记--理解java类加载器以及ClassLoader类

    类加载器概述: java类的加载是由虚拟机来完成的,虚拟机把描述类的Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成能被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制 ...

  9. 深入理解Spring--动手实现一个简单的SpringIOC容器

    接触Spring快半年了,前段时间刚用Spring4+S2H4做完了自己的毕设,但是很明显感觉对Spring尤其是IOC容器的实现原理理解的不到位,说白了,就是仅仅停留在会用的阶段,有一颗想读源码的心 ...

随机推荐

  1. 边工作边刷题:70天一遍leetcode: day 85-1

    Inorder Successor in BST 要点:这题要注意的是如果不是BST,没法从树结构上从root向那边找p,只能遍历.而根据BST,可以只走正确方向 如果不检查right子树,可以从ro ...

  2. poj 1463 Strategic game DP

    题目地址:http://poj.org/problem?id=1463 题目: Strategic game Time Limit: 2000MS   Memory Limit: 10000K Tot ...

  3. 2014 Super Training #9 F A Simple Tree Problem --DFS+线段树

    原题: ZOJ 3686 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3686 这题本来是一个比较水的线段树,结果一个ma ...

  4. nvl函数

    NVL(E1, E2)的功能为:如果E1为NULL,则函数返回E2,否则返回E1本身. 但此函数有一定局限,所以就有了NVL2函数. NVL2(E1, E2, E3)的功能为:如果E1为NULL,则函 ...

  5. String类详解(1)

    首先String是一个类. 1,实例化String类方法. 1)直接赋值:String name="haha"; 2)通过关键字:String name=new String(&q ...

  6. Spring之AOP

    package org.zln.module.test3_aop.interceptor; import org.aspectj.lang.ProceedingJoinPoint; import or ...

  7. MVC3 使用 FusionCharts 做报表

    环境 VS2010+SQL2008 +MVC3 报表思路 1.新建报表需要的数据表,FusionCharts将直接获取数据表的数据进行展示 2.使用SQL代理,通过存储过程定时生成数据表的数据,只添加 ...

  8. 【WPF】无边框窗体

    之前写了一个支持尺寸变换的无边框窗体的一个基窗体,代码如下: public class LBaseWindow : Window { /// <summary> /// 基窗体 /// & ...

  9. poj3984迷宫问题 广搜+最短路径+模拟队列

    转自:http://blog.csdn.net/no_retreats/article/details/8146585   定义一个二维数组: int maze[5][5] = { 0, 1, 0, ...

  10. IPAdr.exe注册机[PY]

    算法还原: a =raw_input('enter the string: \n') num =len(a) All_c=0 for i in range(0,num,1): b =ord(a[i]) ...