SpringBoot项目打包萝卜与坑

一、SpringBoot项目,打成默认的Jar包

应用背景:
该场景较为常见,没有特殊需求时,都可以采用这种打包方式

操作步骤:
SpringBoot项目打成默认的Jar包是最简单的一种打包方式,默认情况下,我们几乎不需要增加任何额外的配置,只要我们执行相关的打包命令即可(maven)mvn clean package就可以完成相应的打包操作;这时,maven会帮助我们将我们的代码和依赖库打成一个Jar包,包名在pom.xml文件中进行配置。打包完成后,我们之间执行java -jar <package.jar> 即可。

补充:SpringBoot打包后的目录格式如下:
SpringBoot Jar

看着东西挺多了,其实我们只要关注2个点即可:第一是目录BOOT-INF,第二是META-INF

二、SpringBoot项目,将源码与依赖库分离打包

应用背景:
在某些时候,我们可能会有一些需求,比如将项目的源码与依赖库和配置分别打包,期望各自相互独立。

真实案例:
58同城自主研发了RPC框架"SCF",我们使用SpringBoot调用SCF相关的服务,然后做相关处理和业务逻辑,这都不是重点;重点是SCF在启动时要对所有其依赖的Jar包进行扫描,如果没能扫描到,那就SCF的类加载器将无法加载这些Jar包,并且最重要的来了, SCF的扫描是基于文件系统路径的 ,也就是说,此时,如果我们按照第一种方式打包的话,SCF在去扫描其依赖的Jar包时,真实的Jar包是在我们打好的工程Jar包里面——我习惯把它称之为“jar中jar”,这时候按照文件系统路径去扫描的时候,是无法加载到这些Jar的。最直观的结果就是我们的工程打包之后无法运行。那么此时我们要如何解决这个问题呢?
问题定位:遇到实现方式为基于文件系统扫描时,遇到了jar中Jar这种情况,就将无法正确加载相关的类
问题分析:避免Jar中Jar,我们需要避免Jar中Jar找不到的情况
解决方案:1.打包时分离源码与类库及配置,保证加载的东西都是在文件系统路径下可寻找的。2.如果是Web项目考虑打成war包,war包在部署到web容器中后,会自动解压,也就变成了单层Jar,也能避免Jar中Jar

如何分离打包呢?

一、首先引入Maven的Assembly插件maven-assembly-plugin,配置细节如下:

<plugin>
	<artifactId>maven-assembly-plugin</artifactId>
	<configuration>
		<appendAssemblyId>false</appendAssemblyId>
		<descriptors>
			<descriptor>src/main/resources/assembly.xml</descriptor>
		</descriptors>
	</configuration>
	<executions>
		<execution>
			<id>make-assembly</id>
			<phase>package</phase>
			<goals>
				<goal>single</goal>
			</goals>
		</execution>
	</executions>
</plugin>

然后创建一个assembly.xml配置文件,增加我们打包的细节配置的描述:

1.我们把最终的工程打成一个ZIP包
2.这个ZIP包中包含了几个元素: 打包格式,我们选择的是ZIP,ZIP里包含的是我们的配置文件,我们工程的配置文件一般放在src/main/resources中,所以这里我们把这个文件夹下的全部配置都打加进来,输出的包名为config,bin,lib同理,lib中包含了全部的依赖Jar

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>package</id>
<formats>
    <format>zip</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
    <fileSet>
        <directory>${basedir}/src/main/resources</directory>
        <includes>
            <include>*.*</include>
        </includes>
        <filtered>true</filtered>
        <outputDirectory>${file.separator}config</outputDirectory>
    </fileSet>

    <fileSet>
        <directory>src/main/resources/runScript</directory>
        <outputDirectory>${file.separator}bin</outputDirectory>
    </fileSet>
    <fileSet>
        <directory>${project.build.directory}/lib</directory>
        <outputDirectory>${file.separator}lib</outputDirectory>
        <includes>
            <include>*.jar</include>
        </includes>
    </fileSet>
    <fileSet>
        <directory>${project.build.directory}</directory>
        <outputDirectory>${file.separator}</outputDirectory>
        <includes>
            <include>*.jar</include>
        </includes>
    </fileSet>
</fileSets>
</assembly>

二、引入maven-jar-plugin插件;这里要指定启动类,<useUniqueVersions>false</useUniqueVersions>这个配置是一个非常关键的配置,此处存在着一个大深坑,如果你没遇到过的话…请参考,如果不配置此项,默认值为true,在快照发布后,我们download到本地仓库的Jar包会出现2个,一个是带时间戳的,一个不带,我们引用的是不带时间戳的,但是下载的时候会下载成带时间戳的,这是因为在打包的时候,META-INF中的classpath配置里,引用的Jar包的名称是带时间戳的,这样就会导致最终找不到类。参考下图所示:
snapshot jar

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-jar-plugin</artifactId>
	<configuration>
		<archive>
			<manifest>
				<addClasspath>true</addClasspath>
				<classpathPrefix>lib/</classpathPrefix>
				<mainClass>com.package.SpringbootPassportApplication</mainClass>
				<useUniqueVersions>false</useUniqueVersions>
			</manifest>
		</archive>
	</configuration>
</plugin>

三、引入依赖管理插件maven-dependency-plugin,这个插件主要作用是分类依赖库,简单是说就是把我们依赖的Jar包(lib)打在工程外面,路径就是在${project.build.directory}/lib中,注意:includeScope,如果配置为compile,那么项目依赖的运行时依赖是不会被打包的,在运行时可能会出现类找不到或者其它的异常,所以想要避免这个问题,需要把includeScope配置为runtime。

<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-dependency-plugin</artifactId>
	<executions>
		<execution>
			<id>copy-lib</id>
			<phase>prepare-package</phase>
			<goals>
				<goal>copy-dependencies</goal>
			</goals>
			<configuration>
				<outputDirectory>${project.build.directory}/lib</outputDirectory>
				<overWriteReleases>true</overWriteReleases>
				<overWriteSnapshots>true</overWriteSnapshots>
				<overWriteIfNewer>true</overWriteIfNewer>
				<includeScope>runtime
				</includeScope>
			</configuration>
		</execution>
	</executions>
</plugin>

四、结果
执行打包命令后,ZIP包内容如下所示:,我们解压后,运行java -jar <project.jar>,项目成功运行,且无异常
projectZIP

五、补充(之前踩过的坑)

由于项目本质是一个web项目,此处只是打了Jar包,所以一定要注意的是,在引入项目依赖的时候,不可以排除spring-boot-starter-web中对内置tomcat的依赖,也不可以将spring-boot-starter-tomcat中的scope设置为provided,因为我们打Jar包执行时需要依赖相关的web容器。——有人会问为什么要排除这个依赖,是因为在尝试打war包的时候,对这些配置进行排除,然后切换成Jar后,发现项目运行不起来,排查了很久,浪费了不少时间,也算是自己给自己挖的坑,希望大家在切换的时候避免这个问题。

文章总结:

0.SpringBoot单独打包无法加载Jar包中的配置和Jar
1.SpringBoot分离打包的方法
2.SpringBoot分离打包需要的Maven依赖和配置文件
3.SpringBoot引入快照版本的依赖打包时,META-INF中寻找的classpath值带时间戳

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值