你所不知道的五件事情--JAR文件
这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,讲述了关于JAR的一些应用窍门,值得大家学习。(2010.06.27最后更新)
摘要:许多Java开发者从没有深入思考过JAR--他们只是在将类传到产品服务器之前使用JAR打包这些文件罢了。但JAR并不仅仅是一个被重命名的 ZIP文件。学习如何使用Java归档文件的全部能力,包括打包Spring依赖和配置文件的小窍门。
对大多数Java开发者而言,JAR与它的兄弟们,WAR和EAR,都是一长串Ant或Maven处理后的最终结果。一个标准的过程是将JAR复制到服务器的适应位置(或者,更少见地,复制到用户的机器上),然后就把它遗忘了。
准确地说,JAR能做的远不止存储源代码,但你必须要知道它能做的其它事情,以及怎样去使用它。在本"5 things"系列的分期文章中所介绍的窍门将展示如何制作大部分的Java归档文件(在有些例子中,也会涉及WAR和EAR),特别是在开发时期。
因为有众多的Java开发者在使用Spring(也因为Spring框架展示了一些相对于我们对JAR传统应用的挑战),其中若干窍门是特别针对Spring应用中的JAR文件。
我将以一个标准的Java归档文件处理的例子开始,该例是下面各窍门的基础。
置于JAR中
一般地,在编译源代码之后你会制作一个JAR文件,通过jar命令工具,或更为通用的Ant的jar工作,去把Java代码(已经被包分隔开)归集到单个文件中。这种处理很明了,在此处我就不作展示了,但在本文的后面我将回到JAR文件是如何被构造的这个主题中来。现在,我们只需打包Hello类,这是一个独立运行的控制台工具应用,该应用会向控制台打印一条信息,这无疑是很有用的任务,如清单1所示:
清单1. 用于打包的控制台工具
package com.tedneward.jars;
public class Hello
{
public static void main(String[] args)
{
System.out.println("Howdy!");
}
}
Hello工具并不复杂,但以可执行程序开始,它是探索JAR文件的一个有用的辅助手段。
1. JAR是可执行的
像.NET和C++这样的编程语言,在历史上有操作系统友好方面优势,只需在命令行中引入它们的名字(helloWorld.exe)或在GUI Shell中双击它们的图标就会启动这些应用。然而在Java编程中,启动器程序--java--引导JVM运行,而后我们必须传入一个命令行参数 (com.tedneward.Hello)用于指定将要启动的含有main()方法的类。
这些额外的步骤使得很难为Java创建用户友好的应用。不仅仅是由于最终用户必须在命令行中键入所有的这些元素(很多用户都想避免这种情况),而且他或她会由于某种原因打错字并得到一个晦涩的错误返回。
解决方案就是使JAR文件"可执行",以便在执行JAR文件时,能让Java启动器自动地知道启动哪个类。我们所需要做的只是在JAR文件的manifest(JAR文件META-INF中的MENIFEST.MF文件)中引入一个属性,例如:
清单2. 显示入口点
Main-Class: com.tedneward.jars.Hello
manifest就是一组名值对。因为mainfest有时候对回车和空格比较敏感,所以在制作JAR时使用Ant去生成该文件要方便些。在清单3中,我就在Ant的jar任务中使用了manifest元素去指定要生成的manifest:
清单3. 构建入口点
<target name="jar" depends="build">
<jar destfile="outapp.jar" basedir="classes">
<manifest>
<attribute name="Main-Class" value="com.tedneward.jars.Hello" />
</manifest>
</jar>
</target>
现在为了执行JAR文件,用户所需要做的只是在命令行中指定文件名,通过命令java -jar outapp.jar。对于这种情况,在有些GUI Shell中双击JAR文件也是可以的。
2. JAR能够包含依赖信息
Hello工具类的文字似乎已经扩展了,这样对不同的实现的需求就变得很紧急了。像Spring或Guice这样的依赖注入(DI)容器为我们处理了许多细节,但仍有一点儿障碍:
清单4. Hello, Spring world!
package com.tedneward.jars;
import org.springframework.context.*;
import org.springframework.context.support.*;
public class Hello
{
public static void main(String[] args)
{
ApplicationContext appContext =
new FileSystemXmlApplicationContext("./app.xml");
ISpeak speaker = (ISpeak) appContext.getBean("speaker");
System.out.println(speaker.sayHello());
}
}
因为启动器的-jar选项会被命令行中的-classpath选项所覆盖,那么当你运行上述程序时,Spring需要出现在CLASSPATH中,并且要在环境变量中。幸运地是,JAR允许针对其它JAR依赖的声明出现在manfiest中,这就隐式地创建了CLASSPATH而不需要你去声明,如清单5 所示:
清单5. Hello, Spring CLASSPATH!
<target name="jar" depends="build">
<jar destfile="outapp.jar" basedir="classes">
<manifest>
<attribute name="Main-Class" value="com.tedneward.jars.Hello" />
<attribute name="Class-Path"
value="./lib/org.springframework.context-3.0.1.RELEASE-A.jar
./lib/org.springframework.core-3.0.1.RELEASE-A.jar
./lib/org.springframework.asm-3.0.1.RELEASE-A.jar
./lib/org.springframework.beans-3.0.1.RELEASE-A.jar
./lib/org.springframework.expression-3.0.1.RELEASE-A.jar
./lib/commons-logging-1.0.4.jar" />
</manifest>
</jar>
</target>
注意到Class-Path属性包含有该应用的依赖相对于JAR的引用路径。你也可以写绝对引用路径,或者完全不需要前缀,在这种情况下就要假设这些依赖 JAR文件与应用程序的JAR文件在同一目录下。
不幸地是,Ant的Class-Path属性对应的value属性必须出现在一行中,因为JAR manfiest无法应对多个Class-Path属性,所以,所有的依赖必须出现在manifest文件的同一行中。可以肯定的是,这种做法很丑陋,但为了能使用命令java -jar outapp.jar,这是值得的。
3. 可隐式地引用JAR
如果你有多个不同的命令行工具(或其它的应用)需要使用Spring框架的JAR文件,那么将这些Spring JAR文件置于公共路径中,以便所有的工具类都能被引用到。这样做就能避免文件系统中满是JAR文件的多份拷贝。Java运行时环境的公共JAR文件路径,即大家所知的"扩展目录",默认是位于JRE安装路径下的lib/ext子目录中。
JRE是一个可定制的路径,但仍然很少在一个给定的Java环境中定制该路径使我们能够安全放心地假设lib/ext是一个存放JAR文件的安全地方,该目录中的JAR文件将默认出现在Java运行时环境的CLASSPATH中。
4. Java 6允许类路通配符
作为一种避免庞大CLASSPATH环境变量(Java开发者在多年前就已经抛弃它了)和/或命令行-classpath参数的努力,Java 6引入了类路径通配符选项。与在启动时必须在一个参数中显示地列出每个JAR文件不同,类路径通配符允许你通过lib/*来指定该目录下的所有JAR文件 (但不允许递归其子目录中的JAR文件)设置到类路径中。
不幸地是,类路径通配符并不能支持之前讨论过的Class-Path属性manifest条目。为了某些开发者任务,例如代码生成或分析工具,使用类路径通配符可以更方便地启动Java应用程序(包括服务器)。
5. JAR不只是包含代码
就像Java生态系统中的许多组成部分那样,Spring依赖一个配置文件,该文件描述了如何去构建运行环境。如前所述,Spring依赖app.xml 文件,该文件与JAR文件存在于同一个目录下--但经常地,开发者们会忘记复制JAR文件边上的配置文件。
sysadmin会编辑某些配置文件,但也有大量的配置文件(如Hibernate映射文件)在sysadmin的域之外,这将导致发布错误。一种明智的解决方案就是将代码与配置文件打包在一起--这是可行的,因为JAR本质上就是改头换面的ZIP。只需将配置文件包含在Ant任务中,或使用jar命令去构建JAR文件。
不仅仅是配置文件,JAR还可以包含其它类型的文件。例如,如果我的SpeakEnglish组件想到访问一个属性文件,那么我会像清单6那样进行设置:
清单6. 随机响应
package com.tedneward.jars;
import java.util.*;
public class SpeakEnglish
implements ISpeak
{
Properties responses = new Properties();
Random random = new Random();
public String sayHello()
{
// Pick a response at random
int which = random.nextInt(5);
return responses.getProperty("response." + which);
}
}
将responses.properties置入JAR文件就意味着,不需要操心有太多文件要随JAR文件一同部署了。要做到这些,只需在制作JAR的过程中包含上responses.properties文件。
一旦你在JAR中存放了配置文件,你就可能就想着如何得到它。如果你所想要的数据位于同一JAR文件中,可以让类的ClassLoader将该文件作为 JAR文件中的"资源"进行查找,使用ClassLoader的getResourceAsStream()方法,如清单7所示:
清单7. ClassLoader定位资源
package com.tedneward.jars;
import java.util.*;
public class SpeakEnglish
implements ISpeak
{
Properties responses = new Properties();
//
public SpeakEnglish()
{
try
{
ClassLoader myCL = SpeakEnglish.class.getClassLoader();
responses.load(
myCL.getResourceAsStream(
"com/tedneward/jars/responses.properties"));
}
catch (Exception x)
{
x.printStackTrace();
}
}
//
}
结论
本文涵盖了关于JAR的多数Java开发者最不知道的5件事情--至少基于历史和轶事证据可以这么认为。注意,所有这些与JAR相关的窍门也同样适用于 WAR。有些窍门(特别是Class-Path和Main-Class属性)对于WAR不完全正确,因为Servlet环境会获取目录中的全部内容并有一个预定义的入口点。但综合来看,这些窍门还是让我们超越了这样一种范式:"好吧,让我们开始复制目录下的所有文件吧..."。除了这些,他们还使得部署 Java应用变得非常容易。
分享到:
相关推荐
还在为找不到jar文件烦心吗,不用了到我空间来有你想要的,持续更新。。。 commons-beanutils-1.8.2.jar,commons-codec-1.4.jar,commons-collections-3.2.1.jar,commons-dbcp-1.2.2.jar,commons-digester-2.0....
commons-codec-1.9.jar文件,java工程导入库文件
在给定的文件列表中,我们可以看到四个关键的JAR(Java Archive)文件,它们在Java EE应用开发中扮演着重要角色: 1. **javaee.jar**:这是Java EE的API库,包含了Java EE规范定义的所有接口和类。开发者可以通过这...
这里提到的`tomcat-juli.jar`、`tomcat-juli-adapters.jar`和`log4j-1.2.12.jar`是与Tomcat日志输出密切相关的组件。 1. **tomcat-juli.jar**: Tomcat JULI(Java Util Logging Implementation)是Tomcat自定义的...
已在附件供大家下载,若是你所需要的东西,那就请投个票、说句鼓励的话,我就满足了。 commons-beanutils.jar commons-collections.jar commons-digester.jar jsf-api.jar jsf-impl.jar jstl.jar standard.jar
这里提到的三个JAR文件——`commons-beanutils.jar`、`commons-collections-3.1.jar`和`commons-pool-1.2.jar`,都是Apache Commons项目的一部分,分别涉及Bean操作、集合操作和对象池化。 **1. `commons-beanutils...
`commons-fileupload-1.3.3.jar` 和 `commons-io-2.5.jar` 是Apache Commons项目中的两个重要库,它们提供了强大的文件上传功能,使得开发者可以轻松地处理用户通过表单提交的文件。 Apache Commons FileUpload是...
总的来说,`commons-fileupload-1.3.3.jar`和`commons-io-2.6.jar`是Java开发中不可或缺的工具,它们大大简化了文件上传的实现,同时也提供了良好的错误处理和资源管理机制,使得文件上传操作变得更加可靠和高效。...
junit-4.12单元测试框架必须引用的jar包, 所需jar包, 除了junit-4.12.jar之外, hamcrest-core-1.3.jar 和 hamcrest-library-1.3.jar 这两个包也是必须的, 下载后解压包里包含了这两个jar包
4. **异常处理**:库中包含了处理上传过程中可能出现的各种异常,如文件大小超出限制、文件类型不匹配等。 `commons-io-1.3.2.jar`则是Apache Commons IO项目的一个较旧版本,它提供了一系列通用的IO操作工具类,...
jackson -core -2.2.3 .jar json jackson 2.2.3 java处理json需要的jar包 内包含文件: jackson-core-2.2.3.jar jackson-core-2.2.3-sources.jar jackson-core-2.2.3-javadoc.jar jackson-annotations-2.2.3.jar ...
总的来说,`commons-fileupload-1.1.1.jar` 和 `commons-io-1.2.jar` 是Java Web开发中实现文件上传功能不可或缺的工具,它们提供了强大且灵活的文件上传处理能力。结合使用这两个库,开发者可以轻松地在Java应用...
`java-property-utils-1.9.jar` 可能用于读取自定义的配置文件,例如,你可能希望根据不同的环境设置不同的CORS策略,这时可以将策略写入一个属性文件,然后使用`java-property-utils`来读取并设置过滤器的参数。...
标题中的"jna-4.2.2.jar"和"jna-platform-4.2.2.jar"是Java Native Access (JNA)框架的两个关键组件的版本号为4.2.2的JAR文件。JNA是Java平台上的一个开源库,它允许Java代码直接调用操作系统提供的原生函数,而无需...
这两个JAR文件,`commons-dbcp-1.4.jar` 和 `commons-pool-1.5.6.jar`,在Java应用中起到了重要的角色,特别是对于那些需要高效管理数据库连接的大型系统。 Apache Commons DBCP是Apache Commons项目的一部分,它...
jarjar-1.4.jar是该工具的一个版本,虽然现在可能已有更新的版本,但1.4版本仍被许多开发者用于处理特定的兼容性问题。 使用jarjar.jar的步骤通常包括以下几个部分: 1. **规则定义**:首先,我们需要定义规则文件...
例如,你可以使用`FTPClient`类建立连接,`FTPFile`类来处理服务器上的文件信息,以及`FTPReply`类来解析服务器的响应代码。 `jakarta-oro-2.0.8.jar`则是Jakarta ORO(Oracle Regular Expressions for Java)库,...
标题"jsp-api.jar和servlet-api.jar"提到了两个关键的Java Web开发中的库文件,它们是JavaServer Pages (JSP) 和Servlet技术的标准接口定义。这两个API是Java EE (Enterprise Edition) 平台的重要组成部分,用于构建...
这个压缩包包含了Jackson库的三个核心组件的2.2.3版本:`jackson-annotations-2.2.3.jar`、`jackson-core-2.2.3.jar` 和 `jackson-databind-2.2.3.jar`。 1. **jackson-annotations-2.2.3.jar**: 这个模块提供了...
解决no such provider: BC 问题所需的JAR 在jdk中的jre\lib\security修改java.security文件, security.provider.6=com.sun.security.sasl.Provider 下面添加 security.provider.7=org.bouncycastle.jce.provider....