Crack jar
jar包是啥
jar(java archieve file),java归档文件
与zip比较
其内容和zip文件非常相似,甚至可以直接使用360压缩这种软件解压
唯一的区别就是,jar中有一个META-INF目录,这里面有一个MANIFEST.MF文件,该文件可能长这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Manifest-Version: 1.0 Implementation-Title: challenge Implementation-Version: 0.0.1-SNAPSHOT Built-By: shiyu Implementation-Vendor-Id: io.tricking Spring-Boot-Version: 2.1.0.RELEASE Main-Class: org.springframework.boot.loader.JarLauncher Start-Class: io.tricking.challenge.ChallengeApplication Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Created-By: Apache Maven 3.5.3 Build-Jdk: 1.8.0_102 Implementation-URL: https://projects.spring.io/spring-boot/#/spring-bo ot-starter-parent/challenge
|
其中最关键的是Main-Class信息,即jar包的入口点
有些jar包可以通过java -jar a.jar
这种命令被执行,此时Main-Class就指定从哪个类的Main函数开始执行
可以理解为,这个META-INF/MANIFEST.MF就是一个清单,表明该jar包的概要信息.
jar包其他文件主就是class字节码文件等
打包目的
打jar包的目的,实际上和c/c++中制作.lib静态库或者.dll动态库一样,就一个文件,方便别人拷贝使用
模块化,标准化
只要写好文档,告诉别人如何调用jar包中的类和函数即可
maven管理的第三方库,都是使用jar包的形式提供的
jar vs war
war包是Sun公司提出的web应用程序格式
两者相同点,都是一个压缩包
不同点,目录结构有区别
凡是能打war包的东西,必然能够打jar包
如何打包
源代码
源代码结构
1 2 3 4 5 6 7 8 9 10
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# tree src -f src └── src/top └── src/top/dustball ├── src/top/dustball/Dao │ └── src/top/dustball/Dao/User.java └── src/top/dustball/Main.java
3 directories, 2 files
|
其中User.java是一个pojo
1 2 3 4 5 6 7 8 9 10 11 12 13
| package top.dustball.Dao; public class User { String username; String password;
public User(String username,String password){ this.username=username; this.password=password; } public String toString(){ return "["+username+","+password+"]"; } }
|
Main.java是程序入口,依赖User类
1 2 3 4 5 6 7 8
| package top.dustball; import top.dustball.Dao.User; public class Main { public static void main(String[] args) { User user = new User("vader", "sjh"); System.out.println(user); } }
|
javac编译
打入jar包的都是class字节码文件,没有java源文件,因此首先需要javac编译
可以使用javac <sourcefile> -d out
将所有字节码文件打包到out目录下
这里sourcefile没有找到一个一劳永逸的方法
每个javac命令最多只能编译一个目录下的所有java文件
1 2 3 4
| top.dustball/ Dao/ User.java Main.java
|
这里Dao/User.java和Main.java在不同的目录下,并且Main.java中依赖User.java,因此需要写一个很长的编译命令
1 2
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# javac src/top/dustball/Dao/*.java src/top/dustball/*.java -d out
|
更好的方法是将所有需要编译的源代码文件写到一份清单中,然后让javac根据清单工作,比如在根目录下写一个JavaList.txt
1 2
| src/top/dustball/Dao/User.java src/top/dustball/Main.java
|
此时在此处执行javac,就是javac的当前工作目录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# javac @JavaList.txt -d out
┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# ls bin JavaList.txt lib out README.md src
┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# tree -f out out └── out/top └── out/top/dustball ├── out/top/dustball/Dao │ └── out/top/dustball/Dao/User.class └── out/top/dustball/Main.class
3 directories, 2 files
|
此时在根目录下已经生成了out目录,保留了包结构和所有class文件,下一步就是打jar包了
jar打包
1 2 3 4 5 6 7 8 9
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# jar -cvf Main.jar out/top/dustball/Main.class out/top/dustball/Dao/User.class added manifest adding: out/top/dustball/Main.class(in = 523) (out= 341)(deflated 34%) adding: out/top/dustball/Dao/User.class(in = 897) (out= 485)(deflated 45%)
┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# ls *.jar Main.jar
|
此时在jar工作目录,也就是跟目录下,生成了Main.jar其中都有啥呢?
1 2 3 4 5 6
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# jar -tf Main.jar META-INF/ META-INF/MANIFEST.MF out/top/dustball/Main.class out/top/dustball/Dao/User.class
|
两个class都已经打包进入了Main.jar
关键在这个META-INF/MANIFEST.MF
提取Main.jar之后,该文件长这样
1 2
| Manifest-Version: 1.0 Created-By: 11.0.17 (Debian)
|
唯一的有效信息是jdk版本11.0.17,Debian操作系统
没有Main-Class入口点信息,显然直接java -jar是不可以执行的
1 2 3
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# java -jar Main.jar no main manifest attribute, in Main.jar
|
没有主属性清单
怎么改呢?
写到这里发现一个重大错误,包名不统一了
啥意思呢?
之前javac,jar的工作目录都是根目录,而包在src路径之下,是根目录的子目录
1 2 3 4 5 6
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# jar -tf Main.jar META-INF/ META-INF/MANIFEST.MF out/top/dustball/Main.class out/top/dustball/Dao/User.class
|
此处out路径被当作了包的最外层,
可能是jar打包认为out也是包路径,但是class自己认为out不是包路径,此后就出错了
而实际上应该让包从top开始,如下:
1 2 3 4 5 6
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# jar -tf Main.jar META-INF/ META-INF/MANIFEST.MF top/dustball/Main.class top/dustball/Dao/User.class
|
按照这样修改之后,在META-INF/MANIFEST.MF
中这样写:
1 2 3 4
| Manifest-Version: 1.0 Created-By: 11.0.17 (Debian) Main-Class: top.dustball.Main
|
这就指定好了入口类
然后重新打包,这一次需要-m指定META-INF/MANIFEST.MF
文件,作为jar包清单
1 2 3 4 5 6
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire] └─# jar -cvfm Main.jar META-INF/MANIFEST.MF top/dustball/Main.class top/dustball/Dao added manifest adding: top/dustball/Main.class(in = 523) (out= 341)(deflated 34%) adding: top/dustball/Dao/(in = 0) (out= 0)(stored 0%) adding: top/dustball/Dao/User.class(in = 897) (out= 485)(deflated 45%)
|
执行
1 2 3
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire] └─# java -jar Main.jar [vader,sjh]
|
更灵活的打包方式
javac编译时,使用清单列出需要编译的源文件是一个好办法
jar打包时有没有更方便的方法呢?
刚才我们有两个打jar包的方式,其一是直接指定所有class文件
1
| jar -cvf Main.jar out/top/dustball/Main.class out/top/dustball/Dao/User.class
|
此时Manifest文件是自动生成的,不包含入口点等信息
需要编写Manifest文件之后重新打包
1
| jar -cvfm Main.jar META-INF/MANIFEST.MF top/dustball/Main.class top/dustball/Dao
|
奇怪的是,当时Dao/User.class没有敲上,敲到Dao就停了,也成功打包了
现在把修改后的META-INF/MANIFEST.MF整体拷贝到out目录下,现在的out目录:
1 2 3 4 5 6 7 8 9 10 11 12
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree/out] └─# tree -f . ├── ./META-INF │ └── ./META-INF/MANIFEST.MF └── ./top └── ./top/dustball ├── ./top/dustball/Dao │ └── ./top/dustball/Dao/User.class └── ./top/dustball/Main.class
4 directories, 3 files
|
在此处指定META-INF/MANIFEST.MF并打包
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree/out] └─# jar -cvfm Main.jar META-INF/MANIFEST.MF * added manifest ignoring entry META-INF/ ignoring entry META-INF/MANIFEST.MF adding: top/(in = 0) (out= 0)(stored 0%) adding: top/dustball/(in = 0) (out= 0)(stored 0%) adding: top/dustball/Dao/(in = 0) (out= 0)(stored 0%) adding: top/dustball/Dao/User.class(in = 897) (out= 485)(deflated 45%) adding: top/dustball/Main.class(in = 523) (out= 341)(deflated 34%)
┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree/out] └─# ls Main.jar META-INF top
|
执行jar包
1 2 3
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree/out] └─# java -jar Main.jar [vader,sjh]
|
更更灵活的打包方式
在打包的时候不指定入口点而在执行的时候指定入口点
out目录状态:
1 2 3 4 5 6 7 8 9 10
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree/out] └─# tree -f . └── ./top └── ./top/dustball ├── ./top/dustball/Dao │ └── ./top/dustball/Dao/User.class └── ./top/dustball/Main.class
3 directories, 2 files
|
直接再次处打jar包
1 2 3 4 5 6 7 8 9
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree/out] └─# jar -cvf Main.jar * added manifest adding: top/(in = 0) (out= 0)(stored 0%) adding: top/dustball/(in = 0) (out= 0)(stored 0%) adding: top/dustball/Dao/(in = 0) (out= 0)(stored 0%) adding: top/dustball/Dao/User.class(in = 897) (out= 485)(deflated 45%) adding: top/dustball/Main.class(in = 523) (out= 341)(deflated 34%)
|
运行时指定入口点
1 2 3
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree/out] └─# java -cp Main.jar top.dustball.Main [vader,sjh]
|
依赖jar包
现在jar包中有一个top.dustball.User类,在Test类中调用之
在Test路径下有一个Main.jar这个包,还有测试类Test
1 2 3
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/Test] └─# ls Main.jar Test.java
|
其中Test.java试图调用User类
1 2 3 4 5 6 7 8 9 10 11 12
| package Test;
import top.dustball.Dao.User;
public class Test { public static void main(String[] args) { User user = new User("vader", "sjh"); System.out.println(user); } }
|
编译链接要加上-cp选项,指定依赖项
1 2
| --class-path <path>, -classpath <path>, -cp <path> Specify where to find user class files and annotation processors
|
1
| PS C:\Users\86135\Desktop\empire\Test> javac Test.java -cp Main.jar
|
由于Test.java有一个包Test,因此需要退到上级目录执行
1 2 3
| PS C:\Users\86135\Desktop\empire\Test> cd .. PS C:\Users\86135\Desktop\empire> java Test/Test [vader,sjh]
|
命令
jar命令
jar命令是jdk的工具,该exe文件在jdk/bin目录下,需要将该bin目录添加到系统环境变量path,也就是配置jdk环境变量
jar -tf
列表列出jar包结构
1 2 3 4 5 6
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# jar -tf Main.jar META-INF/ META-INF/MANIFEST.MF out/top/dustball/Main.class out/top/dustball/Dao/User.class
|
jar -xf
提取jar包文件,效果等同于解压
1 2 3 4 5 6 7 8 9 10 11
| PS C:\Users\86135\Desktop\javacon> jar -xf challange.jar PS C:\Users\86135\Desktop\javacon> ls
Directory: C:\Users\86135\Desktop\javacon
Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 2018/11/6 13:34 BOOT-INF d---- 2018/11/6 13:34 META-INF d---- 2018/11/6 13:34 org -a--- 2023/1/26 15:21 18134425 challange.jar
|
javac命令
javac java编译器,用于将源代码java文件编译为class字节码文件
javac可以类比为gcc或者g++
javac -d
指定class文件输出位置,保留包结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# tree src -f src └── src/top └── src/top/dustball ├── src/top/dustball/Dao │ └── src/top/dustball/Dao/User.java └── src/top/dustball/Main.java
3 directories, 2 files
┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# javac src/top/dustball/Dao/*.java src/top/dustball/*.java -d out
┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# ls bin lib out README.md src
┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree] └─# tree out -f out └── out/top └── out/top/dustball ├── out/top/dustball/Dao │ └── out/top/dustball/Dao/User.class └── out/top/dustball/Main.class
3 directories, 2 files
|
javac -cp
链接外部依赖
1
| PS C:\Users\86135\Desktop\empire\Test> javac Test.java -cp Main.jar
|
java命令
java -jar
执行自带入口点的jar包
java -cp
运行时指定jar包入口点
1 2 3
| ┌──(root㉿Executor)-[/mnt/c/Users/86135/Desktop/empire/empiree/out] └─# java -cp Main.jar top.dustball.Main [vader,sjh]
|