`
houchangxi
  • 浏览: 64619 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论
阅读更多

对于大多数 Java 开发人员来说,JAR 文件及其 “近亲” WAR 和 EAR 都只不过是漫长的 Ant 或 Maven 流程的最终结果。标准步骤是将一个 JAR 复制到服务器(或者,少数情况下是用户机)中的合适位置,然后忘记它。

事实上,JAR 能做的不止是存储源代码,您应该了解 JAR 还能做什么,以及如何进行。在这一期的 5 件事 系列中,将向您展示如何最大限度地利用 Java Archive 文件(有时候也可是 WAR 和 EAR),特别是在部署时。

由于有很多 Java 开发人员使用 Spring(因为 Spring 框架给传统的 JAR 使用带来一些特有的挑战),这里有几个具体技巧用于在 Spring 应用程序中处理 JAR 。


我将以一个标准 Java Archive 文件产生过程的简单示例开始,这将作为以下技巧的基础。

把它放在 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++ 这类语言一直是 OS 友好的,只需要在命令行(helloWorld.exe )引用其名称,或在 GUI shell 中双击它的图标就可以启动应用程序。然而在 Java 编程中,启动器程序 — java — 将 JVM 引导入进程中,我们需要传递一个命令行参数(com.tedneward.Hello )指定想要启动的 main() 方法的类。

这些附加步骤使使用 Java 创建界面友好的应用程序更加困难。不仅终端用户需要在命令行输入所有参数(终端用户宁愿避开),而且极有可能使他或她操作失误以及返回一个难以理解的错误。

这个解决方案使 JAR 文件 “可执行” ,以致 Java 启动程序在执行 JAR 文件时,自动识别哪个类将要启动。我们所要做的是,将一个入口引入 JAR 文件清单文件(MANIFEST.MF 在 JAR 的 META-INF 子目录下),像这样:


清单 2. 展示入口点!

				
Main-Class: com.tedneward.jars.Hello

 

这个清单文件只是一个名值对。因为有时候清单文件很难处理回车和空格,然而在构建 JAR 时,使用 Ant 来生成清单文件是很容易的。在清单 3 中,使用 Ant jar 任务的 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)容器可以为我们处理许多细节,但是仍然有点小问题:修改代码使其含有 DI 容器的用法可能导致清单 4 所示的结果,如:


清单 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());
    }
}

 

关于 Spring 的更多信息

这个技巧将帮助您熟悉依赖项注入和 Spring 框架。如果您需要温习其他主题,见 参考资料

由于启动程序的 -jar 选项将覆盖 -classpath 命令行选项中的所有内容,因此运行这些代码时,Spring 必须是在 CLASSPATH 环境变量中。幸运的是,JAR 允许在清单文件中出现其他的 JAR 依赖项声明,这使得无需声明就可以隐式创建 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 在同一个目录下。

不幸的是,value 属性和 Ant Class-Path 属性必须出现在同一行,因为 JAR 清单文件不能处理多个 Class-Path 属性。因此,所有这些依赖项在清单文件中必须出现在一行。当然,这很难看,但为了使 java -jar outapp.jar 可用,还是值得的!


3. JAR 可以被隐式引用

如果有几个不同的命令行实用工具(或其他的应用程序)在使用 Spring 框架,可能更容易将 Spring JAR 文件放在公共位置,使所有实用工具能够引用。这样就避免了文件系统中到处都有 JAR 副本。Java 运行时 JAR 的公共位置,众所周知是 “扩展目录” ,默认位于 lib/ext 子目录,在 JRE 的安装位置之下。

JRE 是一个可定制的位置,但是在一个给定的 Java 环境中很少定制,以至于可以完全假设 lib/ext 是存储 JAR 的一个安全地方,以及它们将隐式地用于 Java 环境的 CLASSPATH 上。


4. Java 6 允许类路径通配符

为了避免庞大的 CLASSPATH 环境变量(Java 开发人员几年前就应该抛弃的)和/或命令行 -classpath 参数,Java 6 引入了类路径通配符 的概念。与其不得不启动参数中明确列出的每个 JAR 文件,还不如自己指定 lib/* ,让所有 JAR 文件列在该目录下(不递归),在类路径中。

不幸的是,类路径通配符不适用于之前提到的 Class-Path 属性清单入口。但是这使得它更容易启动 Java 应用程序(包括服务器)开发人员任务,例如 code-gen 工具或分析工具。


5. JAR 有的不只是代码

Spring,就像许多 Java 生态系统一样,依赖于一个描述构建环境的配置文件,前面提到过,Spring 依赖于一个 app.xml 文件,此文件同 JAR 文件位于同一目录 — 但是开发人员在复制 JAR 文件的同时忘记复制配置文件,这太常见了!

一些配置文件可用 sysadmin 进行编辑,但是其中很大一部分(例如 Hibernate 映射)都位于 sysadmin 域之外,这将导致部署漏洞。一个合理的解决方案是将配置文件和代码封装在一起 — 这是可行的,因为 JAR 从根本上来说就是一个 “乔装的” ZIP 文件。 当构建一个 JAR 时,只需要在 Ant 任务或 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 文件在同一位置,正如前面的例子中提到的那样,不需要费心找出 JAR 文件的位置,使用 JarFile 对象就可将其打开。相反,可以使用类的 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 中,作为一个 InputStream 获取(通过 ClassLoader ),并通过您喜欢的方式使用。


结束语

本文涵盖了关于 JAR 大多数开发人员所不知道的 5 件最重要的事 — 至少基于历史,有据可查。注意,所有的 JAR 相关技巧对于 WAR 同样可用,一些技巧(特别是 Class-PathMain-Class 属性)对于 WAR 来说不是那么出色,因为 servlet 环境需要全部目录,并且要有一个预先确定的入口点,但是,总体上来看这些技巧可以使我们摆脱 “好的,开始在该目录下复制......” 的模式,这也使得他们部署 Java 应用程序更为简单。

本系列的下一个主题是:关于 Java 应用程序性能监视您不知道的 5 件事。

分享到:
评论

相关推荐

    jar命令知识的总结

    【描述】:本文旨在为初学者提供一个全面的jar命令教程,涵盖其基本概念、用途、特点以及相关配置文件的解析。 【标签】:Java, jar命令, JAR文件格式, META-INF, Manifest.mf 【正文】: Java的jar命令是开发者...

    jstl1.1.jar和standard1.1.jar

    想起先前看到的一个知识点:tomcat 6 实现了 servlet 2.5 和JSP2.1的规范相应的 tomcat 5 实现的是 2.4和2.0,想到tomcat5不支持JSTL1.2,于是替换JSTL1.1的包试试看后,问题解决 解决方法:移出工程lib目录中的...

    jar的一些相关文件

    本篇将详细讲解`JAR`文件及其在IT领域的应用,特别是与`myEclipse`集成、Excel操作和SQL Server数据库连接的相关知识点。 首先,`myEclipse`是一款基于`Eclipse`的商业集成开发环境,广泛用于Java应用程序的开发。...

    如何生成jar包

    以下详细介绍生成JAR包的过程及相关知识点。 首先,生成JAR包前需要将Java源文件编译成.class字节码文件。这可以通过Java编译器javac来完成。在上述内容中提到了使用`javac`命令,并且用到了`-d`参数来指定输出的...

    NCSO.jar notes.jar pdfrender.jar JimiProClasses.zip

    标题中的"NCSO.jar"、"notes.jar"和"pdfrender.jar"以及"JimiProClasses.zip"都指向了Java相关的库文件和压缩包。这些文件在IT领域中扮演着重要角色,尤其对于开发和运行Java应用程序至关重要。下面将详细阐述这些...

    jspSmartUpload.jar msbase.jar mssqlserver.jar msutil.jar

    下面将详细阐述这些文件及其相关知识点。 首先,`jspSmartUpload.jar`是用于JavaServer Pages (JSP) 的一个上传组件。它允许用户在网页上方便地上传文件到服务器。这个库通常包含了一系列的类和方法,帮助开发者...

    JSONObject相关jar包和maven管理jar包

    标题"JSONObject相关jar包和maven管理jar包"暗示了我们讨论的主题集中在JSONObject类库的使用上,特别是如何在Java项目中通过jar包和Maven构建工具来管理和利用这个类库。JSONObject是Java编程语言中用于处理JSON...

    Spring 相关jar包

    现在,我们逐一探讨这些关键知识点。 1. **依赖注入(IOC)**:依赖注入是Spring的核心功能,它允许开发者解耦组件间的依赖关系。在传统编程中,对象通常会自行创建所需的依赖对象,但在IOC中,这些依赖由外部容器...

    lib包(jmf.jar,mediaplayer.jar,customizer.jar,multiplayer.jar,sound.jar)

    下面我们将逐一解析这些库文件所代表的知识点。 1. **jmf.jar**:这是Java Media Framework (JMF)的核心库。JMF是一个可扩展的平台,用于处理多媒体数据,如音频、视频和流媒体。它允许开发者播放、捕获、处理和...

    dbeaver oracle连接jar xdb6.jar

    在IT行业中,数据库管理是至关重要的任务之一,而DBeaver作为一款强大的、跨平台的...因此,对于IT专业人员来说,熟悉数据库连接的相关知识,尤其是像DBeaver这样的工具与特定数据库的集成,是必不可少的专业技能。

    ssh常用Jar包

    以下是这些Jar包的核心功能和相关知识点的详细说明: 1. **Struts2**: - Struts2是基于MVC设计模式的Web框架,用于处理HTTP请求并提供强大的动作控制和结果渲染。 - 其核心组件包括:`struts2-core.jar`,提供...

    jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1

    【标题】:“jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1jar1” 可能是指一个以 "jar" 结尾的文件名,这通常代表Java Archive(JAR)文件。JAR文件是Java平台特有的归档格式,它用于收集多个Java...

    activation.jar、jsp-api.jar、servlet-api.jar、mail.jar

    让我们一一解析它们的作用和相关知识点。 1. **activation.jar**: 这个文件包含了JavaBeans Activation Framework (JAF),它是Java平台的一个标准组件,主要用于处理MIME类型的数据,如电子邮件、文件附件等。JAF...

    jstl相关jar包(包含jstljar和standard.jar)

    **JSTL(JavaServer Pages Standard Tag Library)**是Java Web开发中的一种标准标签库,它简化了在JSP页面中处理常见...理解JSTL的各个库以及如何配置和使用它们,对于任何Java Web开发者来说都是必不可少的知识点。

    Jar包 Jar包

    在Java世界中,Jar包是执行Java应用程序的基础,它允许开发者将多个类文件、库和相关资源打包在一起,形成一个可执行单元。 Pie Chart(饼图)是一种常用的数据可视化工具,用于展示各部分占整体的比例关系。在...

    MAC Jar反编译工具

    本文将详细介绍在MAC环境下如何使用Jar反编译工具,并探讨相关知识点。 首先,我们要理解什么是反编译。反编译是将已编译的二进制代码转换回源代码的过程,这对于理解和学习已有的软件,或者在没有源代码的情况下...

    验证JAR简单集合

    标题“验证JAR简单集合”暗示了我们正在讨论与Java应用程序相关的主题,特别是关于JAR(Java Archive)文件的验证和管理。JAR文件是Java平台上的一个特殊格式,用于打包多个类文件、资源文件以及元数据,方便分发和...

    saif相关jar包

    总的来说,"saif相关jar包"涉及的是Java开发中的依赖管理和框架集成知识,特别是如何在Spring框架下使用第三方库。这需要对Java、Spring框架以及可能的saif工具或库有深入的理解,才能有效地利用这些资源进行开发。

    C3P0相关的JAR包

    下面我们将深入探讨C3P0的相关知识点。 1. **数据库连接池的概念** 数据库连接池是应用服务器在启动时预先向数据库申请一定数量的连接,然后将这些连接存储在一个池中,当应用需要与数据库交互时,可以从池中获取...

    Spring入门基础知识带jar包

    Spring入门基础知识带jar包

Global site tag (gtag.js) - Google Analytics