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

  • 相关阅读:
    leetcode5 Longest Palindromic Substring
    leetcode17 Letter Combinations of a Phone Number
    leetcode13 Roman to Integer
    leetcode14 Longest Common Prefix
    leetcode20 Valid Parentheses
    leetcode392 Is Subsequence
    leetcode121 Best Time to Buy and Sell Stock
    leetcode198 House Robber
    leetcode746 Min Cost Climbing Stairs
    tomcat下使用druid配置jnid数据源
  • 原文地址:https://www.cnblogs.com/lz2017/p/14398077.html
Copyright © 2011-2022 走看看