`

你所不知道的五件事情--JAR文件(译)

    博客分类:
  • ali
阅读更多
你所不知道的五件事情--JAR文件

这是Ted Neward在IBM developerWorks5 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应用变得非常容易。

1
0
分享到:
评论
1 楼 cnlw1985 2010-06-29  

相关推荐

    commons-beanutils-1.8.2.jar,commons-codec-1.4.jar

    还在为找不到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导入库文件)

    commons-codec-1.9.jar文件,java工程导入库文件

    javaee.jar,jsf-api.jar,jsf-impl.jar,jstl-1.2.jar

    在给定的文件列表中,我们可以看到四个关键的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 6日志输出

    这里提到的`tomcat-juli.jar`、`tomcat-juli-adapters.jar`和`log4j-1.2.12.jar`是与Tomcat日志输出密切相关的组件。 1. **tomcat-juli.jar**: Tomcat JULI(Java Util Logging Implementation)是Tomcat自定义的...

    JSF开发包:commons-beanutils.jar+commons-collections.jar+commons-digester.jar+jsf-api.jar+jsf-impl.jar+jstl.jar+standard.jar

    已在附件供大家下载,若是你所需要的东西,那就请投个票、说句鼓励的话,我就满足了。 commons-beanutils.jar commons-collections.jar commons-digester.jar jsf-api.jar jsf-impl.jar jstl.jar standard.jar

    commons-fileupload-1.3.3.jar commons-io-2.5.jar

    `commons-fileupload-1.3.3.jar` 和 `commons-io-2.5.jar` 是Apache Commons项目中的两个重要库,它们提供了强大的文件上传功能,使得开发者可以轻松地处理用户通过表单提交的文件。 Apache Commons FileUpload是...

    commons-beanutils.jar commons-collections-3.1.jar commons-pool-1.2.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.6.jar

    总的来说,`commons-fileupload-1.3.3.jar`和`commons-io-2.6.jar`是Java开发中不可或缺的工具,它们大大简化了文件上传的实现,同时也提供了良好的错误处理和资源管理机制,使得文件上传操作变得更加可靠和高效。...

    junit-4.12所需jar包 hamcrest-core-1.3.jar hamcrest-library-1.3.jar

    junit-4.12单元测试框架必须引用的jar包, 所需jar包, 除了junit-4.12.jar之外, hamcrest-core-1.3.jar 和 hamcrest-library-1.3.jar 这两个包也是必须的, 下载后解压包里包含了这两个jar包

    commons-fileupload-1.2.1.jar和commons-io-1.3.2.jar程序文件

    4. **异常处理**:库中包含了处理上传过程中可能出现的各种异常,如文件大小超出限制、文件类型不匹配等。 `commons-io-1.3.2.jar`则是Apache Commons IO项目的一个较旧版本,它提供了一系列通用的IO操作工具类,...

    jackson-core-2.2.3.jar jar包合集

    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上传文件jar包

    总的来说,`commons-fileupload-1.1.1.jar` 和 `commons-io-1.2.jar` 是Java Web开发中实现文件上传功能不可或缺的工具,它们提供了强大且灵活的文件上传处理能力。结合使用这两个库,开发者可以轻松地在Java应用...

    cors-filter-1.7.jar 和 java-property-utils-1.9.jar

    `java-property-utils-1.9.jar` 可能用于读取自定义的配置文件,例如,你可能希望根据不同的环境设置不同的CORS策略,这时可以将策略写入一个属性文件,然后使用`java-property-utils`来读取并设置过滤器的参数。...

    commons-dbcp-1.4.jar和commons-pool-1.5.6.jar

    这两个JAR文件,`commons-dbcp-1.4.jar` 和 `commons-pool-1.5.6.jar`,在Java应用中起到了重要的角色,特别是对于那些需要高效管理数据库连接的大型系统。 Apache Commons DBCP是Apache Commons项目的一部分,它...

    jarjar-1.4.jar

    jarjar-1.4.jar是该工具的一个版本,虽然现在可能已有更新的版本,但1.4版本仍被许多开发者用于处理特定的兼容性问题。 使用jarjar.jar的步骤通常包括以下几个部分: 1. **规则定义**:首先,我们需要定义规则文件...

    apache的FTP包commons-net-1.4.1.jar,jakarta-oro-2.0.8.jar

    例如,你可以使用`FTPClient`类建立连接,`FTPFile`类来处理服务器上的文件信息,以及`FTPReply`类来解析服务器的响应代码。 `jakarta-oro-2.0.8.jar`则是Jakarta ORO(Oracle Regular Expressions for Java)库,...

    jsp-api.jar和servlet-api.jar

    标题"jsp-api.jar和servlet-api.jar"提到了两个关键的Java Web开发中的库文件,它们是JavaServer Pages (JSP) 和Servlet技术的标准接口定义。这两个API是Java EE (Enterprise Edition) 平台的重要组成部分,用于构建...

    jackson-annotations-2.2.3.jar jackson-core-2.2.3.jar jackson-databind-2.2.3.jar

    这个压缩包包含了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**: 这个模块提供了...

    commons-codec-1.3.jar,commons-httpclient-3.1.jar,commons-logging-1.1.jar)

    在提供的标题和描述中提到了三个关键的Java Archive (JAR) 文件,它们是Apache Commons项目的一部分,分别是: 1. **commons-codec-1.3.jar**:这个JAR文件包含了Apache Commons Codec库的1.3版本。Codec库提供了...

    bcprov-jdk16-143.jar和bcprov-jdk15-135.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....

Global site tag (gtag.js) - Google Analytics