一、目的
介绍将代码打包成jar包的四种形式:
- 只打包本项目的代码,不包括依赖的jar包,并且不可直接通过java -jar xxx.jar执行(应用场景:我们日常使用依赖的jar包)
- 只打包本项目的代码,不包括依赖的jar包,并且可以直接通过java -jar xxx.jar执行(应用场景:执行时依赖的jar包存在在本jar包外部,减少jar体积)
- 打包本项目的代码,同时将依赖的jar包解压后的文件复制到本jar包中,可以直接通过java -jar xxx.jar执行(应用场景:直接执行,一个jar包即可搞定)
- 打包本项目的代码,同时将依赖的jar包直接复制到本jar包中,可以直接通过java -jar xxx.jar执行(应用场景:直接执行,一个jar包即可搞定)
PS:3和4的区别是,3在复制时可能不同的jar包中存在相同的资源文件,导致被覆盖掉,需要特殊处理。
接下来详细介绍这四种jar包的打包方式
二、打包不可执行jar包
我们日常通过maven依赖的jar包就是这种不可执行的jar包,这种包有两种打包方式
1、直接执行maven package命令,在target目录下xxx.jar即为我们需要的jar包
2、在pom.xml中配置如下内容,执行maven package命令,在target目录下xxx.jar即为我们需要的jar包
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<!--生成的jar中,不要包含pom.xml和pom.properties这两个文件-->
<addMavenDescriptor>true</addMavenDescriptor>
</archive>
</configuration>
</plugin>
</plugins>
</build>
三、打包可执行jar包
直接执行上一步打包的jar包时会提示:xxx.jar中没有主清单属性。原因是执行java -jar xxx.jar命令时,jvm不知道程序的main方法在哪个类里,所以需要我们打包的时候告知这个启动类的位置,也就是通过MAINFEST.MF来指定启动类的位置(当然也可以通过命令参数指定启动类:java -cp xxx.jar com.MainClass,其中com.MainClass为启动类的全名称)
打包时生成MAINFEST.MF文件,指定启动类的位置。其中com.MainClass为自己启动类的全名称
<packaging>jar</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<!--生成的jar中,不要包含pom.xml和pom.properties这两个文件-->
<addMavenDescriptor>true</addMavenDescriptor>
<manifest>
<mainClass>com.MainClass</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
执行maven package命令,在target目录下xxx.jar即为我们需要的jar包,直接执行java -jar xxx.jar命令即可
四、打包可执行jar包(添加外部jar包中的类)
如果我们在我们的应用程序里依赖并使用了其他的jar包,比如json-lib
maven配置:
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
</dependency>
启动类:
public static void main(String[] args) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("key","value");
System.out.println(jsonObject);
}
这时候按照上一步的方式打包后,执行java -jar xxx.jar命令,会报java.lang.NoClassDefFoundError异常,原因是我们程序里依赖的json-lib.jar包并没有一并打包到jar包里,找不到对应的类。要解决这个问题,我们有两种办法,第一个是把json-lib.jar中的类打包的时候复制到我们的jar包中。第二个办法是直接把json-lib.jar打包到我们的jar包中,第二个办法下一步再介绍。
打包的时候将依赖的jar中的类复制到我们的jar包中:其中com.MainClass为自己启动类的全名称
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.MainClass</mainClass>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
执行maven package命令,打包后的jar包中包含所有依赖的外部jar包中类,如下图所示,xxx.jar是我们需要的jar包(另外一个original-xxx.jar包中不包含外部jar包中的类,和上一步打出的jar包一样):
直接执行java -jar xxx.jar命令即可。
PS:使用这种直接复制类的方式会带来一个问题,就是不同jar包中如果存在多个资源文件,比如很多spring的jar包都含有META-INF/spring.handlers或者META-INF/spring.schemas文件,打包的时候如果存在n个相同资源文件(路径和名称都相同),则会丢失n-1的资源文件,对此场景我们可以使用合并功能,将这n个相同资源文件中的内容合并为一个资源文件(当前还有很多其他处理场景,详情参加:Apache Maven Shade Plugin – Resource Transformers)
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.1</version>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.MainClass</mainClass>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</transformers>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
五、打包可执行jar包(直接添加外部jar包)
上一步遇到的资源文件重复的问题也可以通过直接添加外部jar包来解决:其中com.MainClass为自己启动类的全名称
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.3.3.RELEASE</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.MainClass</mainClass>
</configuration>
</plugin>
</plugins>
</build>
执行maven package命令,打包后的jar包格式,依赖的jar包都直接放在lib目录下:
直接执行java -jar xxx.jar命令即可。
以上四种方式可以根据具体的业务场景选择不同的打包方式!