zoukankan      html  css  js  c++  java
  • 记录一次手动打jar包过程

    一直都有用Maven完成项目的构建,而最近又在学习Gradle构建项目,突然想起自己从来没有手动打过一次jar,便有了本次的尝试与记录

    代码结构如下:

    projectA目录:com.lynu.service.A.java

    projectB目录:cn.lynu.service.B.java

    注意这里的A.java和B.java包路径是不同的,主要是想让这两个类在包路径上有些差异

    最终目标是将A.java打包成projectA.jar(不可执行只作为依赖);B.java类会使用projectA.jar中的A类,而把B.java打包成可执行的jar(有main方法入口,可使用java -jar或者 java -cp运行)

    A.java B.java内容比较简单,只输出一句话,没其他实际意义

    package com.lynu.service;
    
    public class A {
        public void sayHello() {
            System.out.println("jarA方法调用");
        }
    }
    
    package cn.lynu.service;
    
    import com.lynu.service.A;
    
    public class B {
        public static void main(String[] args) {
            System.out.println("jarB main方法调用");
            new A().sayHello();
        }
    }
    

    编译A.java

    因为A.java比较简单,而且没有依赖其他类或jar,直接通过javac编译即可

    cd projectA
    
    javac com/lynu/service/A.java
    

    这样在projectA/com/lynu.service目录下就会有一个A.class

    打包A.java

    使用jar命令进行打包,用法:jar {ctxu}[vfm0Mi] [jar-文件] [manifest-文件]/[main方法所在类] [-C 目录] 文件名 ...

    这个命令有一些参数:

        -c  创建新档案
        -t  列出档案目录
        -x  解压jar,从档案中提取指定的 (或所有) 文件
        -u  更新现有档案
        -v  在标准输出中生成详细输出
        -f  指定档案文件名
        -m  包含指定清单文件中的清单信息
        -n  创建新档案后执行 Pack200 规范化
        -e  为捆绑到可执行 jar 文件的独立应用程序指定应用程序入口点
        -0  仅存储; 不使用任何 ZIP 压缩
        -P  保留文件名中的前导 '/' (绝对路径) 和 ".." (父目录) 组件
        -M  不创建条目的清单文件
        -i  为指定的 jar 文件生成索引信息
        -C  更改为指定的目录并包含其中的文件(可以理解为首先cd到指定目录)
    

    较为常见的就是cvfecvfm,区别在于e:可以指定一个有main方法的类作为可执行jar的程序入口,而m:可以指定自定义的MANIFEST.MF(清单文件),如果没有指定清单文件打包后会自动生成

    因为只打包成不可执行的jar,所以使用cvf即可

    jar cvf projectA.jar com/lynu/service/*.java
    

    执行完命令后会在projectA目录下生成projectA.jar

    编译B.java

    因为B.java中使用了A.java的方法,所以projectA.jar是作为B.java运行的依赖

    这是如果只是简单javac编译B.java会出现错误: 程序包com.lynu不存在或者错误: 找不到符号的问题,主要是编译时找不到com.lynu.service.A这个类,这个问题可以通过加入-cp(-classpath)参数指定所需依赖解决

    cd projectB
    
    javac -cp /projectA/projectA.jar com/lynu/service/B.java
    

    类B的class文件也可以正常生成了

    打包B.java

    需要将B打包成一个可执行jar,就需要指定运行程序的入口,也就是main方法所在

    我们使用jar cvfe命令

    jar cvfe projectB.jar cn.lynu.service.B cn/lynu/service/*.java
    

    可以正常在projectB目录下生成jar

    执行jar

    接下来运行这个可执行jar

    java -jar projectB.jar
    

    会发现出现找不到主类的错误,明明打包的时候指定了主类,怎么会找不到啊?好吧,既然找不到那我们依然加上-cp参数

    java -cp .;/projectA/projectA.jar  -jar projectB.jar
    

    会发现可以正常执行jar,再运行下编译后的B.class看看结果

    java -cp .;/projectA/projectA.jar  cn.lynu.service.B
    

    运行class也没问题,那为什么必须指定-cp?这是因为不指定classpath的话,projectB.jar根本不知道自己依赖的projectA.jar在什么地方,如果觉得每次执行都得要加上-cp比较麻烦,可以通过自定义清单文件MANIFEST.MF帮助我们

    在projectB目录下添加一个MANIFEST.MF文件,文件内容如下

    Manifest-Version: 1.0
    Created-By: 1.8.0_201 (Oracle Corporation)
    Class-Path: /projectA/projectA.jar
    Main-Class: cn.lynu.service.B
    
    
    

    Main-Class:程序入口类,类如果有限定名,则写成全限定名,比如package.classname

    Class-Path:所依赖的jar,如果有多个jar包,则以空格分隔多个jar包,一行太多会报错line too long,这时需要把Class-Path分多行写。注意:从第二行开始,必须以两个空格开头

    文件最后有两行空行不能少

    再次打包B.java

    jar cvfm projectB.jar ./MANIFEST.MF cn/lynu/service/*.java
    

    重新打的jar就可以直接使用java -jar运行了

    java -jar projectB.jar
    

    总结

    在编译B.java时,也在使用-cp指定projectA.jar的位置,实际上将projectA.jar放到JAVA_HOME/jre/lib/ext目录下就不需要显式指定位置了,因为类加载器(ExtClassLoader)会自动加载这个目录下的所有jar

  • 相关阅读:
    linux下最大文件数
    Apache实现Web Server负载均衡
    linux环境变量
    iptables用法
    read 不回显的方法
    Linux间的进程通信;以及子进程的创建
    Linux总结
    自实现部分string类的功能
    C语言 (内存) 四道经典题目
    STL 容器(vector 和 list )
  • 原文地址:https://www.cnblogs.com/lz2017/p/14398077.html
Copyright © 2011-2022 走看看