此文章发布于63
个月前,部分信息可能已经过时
,请自行斟酌确认。
需求背景:
srping boot 2.0+ 打包生产的 fat jar
虽然部署起来很方便,但将所有依赖都打包到一个 jar 包中使得体积也不小(40M+),第一次部署还没问题,之后的更新就很痛苦了,每次发布更新时一般都是自己项目的业务代码发生变化,依赖一般不会变化除非升级或增加了项目依赖。
我们期望的场景是每次更新只上传我们自己的业务模块 jar 包,大小可能只有几十K,这就是今天要讲的 thin jar
如何打包。
微酷研究了三个方案,推荐方案三。
方案一:
看起来非常的官方的方式,使用插件 spring-boot-thin-launcher
,使用起来也是相当的简单。
插件地址:
简单来说微酷对这个插件实现的理解:
- 插件可生成 pom.xml 或 thin.properties 文件保存项目所依赖的包。
- 打包时只将自己项目打包,依赖项目不打包。
- 启动时使用单独的装载器从 pom.xml 或 thin.properties 中读取依赖,然后从 maven 仓库下载依赖到本地。
- 运行自己项目的 Main 函数。
安装:
配置 build.gradle
如下:
buildscript {
ext {
springBootVersion = '2.1.4.RELEASE'
wrapperVersion = '1.0.21.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot.experimental:spring-boot-thin-gradle-plugin:${wrapperVersion}")
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
plugins {
id "io.spring.dependency-management" version "1.0.7.RELEASE"
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'org.springframework.boot.experimental.thin-launcher'
jar.dependsOn = [thinProperties]
// 其它配置略...
配置上插件后就会在 Gradle 的 Tasks 中多出好多任务,都是以 thin***
开头的。
打包:
运行 thinJar
任务即可。生成的 jar 包在 ./build/thin/root
下。
运行:
java -jar weiku-web-1.0.0.jar
放弃:
下载依赖是微酷相当不喜欢的(虽然下载后可以缓存之后就不用再下载了),因为我本地有所依赖的所有 jar 包,为什么不让用的,并且一上午大把的时候都在研究这个问题最终也没找到答案,于是放弃这个方案了。
另外由于我的项目是多 module 于是引用的自己的依赖没有上传到 maven 仓库根本就找不到,所以我们项目最终也没有用这个方案运行成功。(可能有方法配置但我放弃继续研究了,英文不好的我的官方的 Readme 读了一遍已累爬。)
我的提问:https://github.com/spring-projects-experimental/spring-boot-thin-launcher
方案二:
又找到一个方法,看起来是超级的简单。
标题:Spring Boot 用gradle bootJar打包瘦身(外置依赖jar)
原文:https://my.oschina.net/formatkm/blog/1822900
方法二旧方法(原作者已经废弃):
网上 SpringBoot 打包瘦身的教程很多,但基本都是 Maven 的。一般公共库都外置,不需要打包进可执行的 jar 中,这样每次发布都不用传几十兆的文件。用 gradle 的 bootJar 实际很简单,方法就是把 compile
依赖替换为 compileClasspath
。 compileClasspath
的依赖不会被打包进可运行的jar中。
//打包进jar
//compile("org.springframework.boot:spring-boot-starter-freemarker")
//只打包自己的代码
compileClasspath("org.springframework.boot:spring-boot-starter-web")
另外可以用 complie 先配置好,导出依赖的jar到一个目录
task copyToLib(type: Copy) {
into "$buildDir/libs/lib"
from configurations.runtime
}
导出后又改为compileClasspath
方法二的新方法:
修改 bootJar
如下,仅此而已:
bootJar {
classifier = 'boot'
excludes = ["*.jar"]
}
运行的时候用 -Djava.ext.dirs=指定依赖库的路径
。
java -Djava.ext.dirs=./lib -jar bootrun.jar
微酷实践:
微酷的项目用方案二的新方法能启动起来,大多数页面正常,但有的页面出错,如访问 MSSQL 数据库时出错:
服务器异常:驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“java.lang.RuntimeException: Could not generate DH keypair”。 ClientConnectionId:eb5aa5f7-8ed4-4bb0-99b8-a70a936640c5
针对这个问题搜索找到一个文章说:
-Djava.ext.dirs 会覆盖 Java 本身的 ext 设置,java.ext.dirs 指定的目录由 ExtClassLoader 加载器加载,如果您的程序没有指定该系统属性,那么该加载器默认加载 $JAVA_HOME/jre/lib/ext 目录下的所有 jar 文件。但如果你手动指定系统属性且忘了把$JAVA_HOME/jre/lib/ext 路径给加上,那么 ExtClassLoader 不会去加载 $JAVA_HOME/lib/ext 下面的jar文件,这意味着你将失去一些功能,例如java自带的加解密算法实现。原文:https://blog.csdn.net/cyony/article/details/74375251
于是微酷将电脑C:\Program Files\Java\jdk1.8.0_121\jre\lib\ext
下的 13 个 jar 文件拷贝到项目发布后的 libs
目录中,竟然神奇的运行正常了。
其实应该继续研究如何合并两个位置的 jar 包来解决这个问题,那么就完美了。
但微酷又找到了完美的方案三,于是这个问题就不再继续研究了。
方案三(推荐):
又找到这种方法,看起来不错,运行时也不需要加 java.ext.dir
或 loader.path
参数。
原标题:spring boot + gradle打包bootJar分离lib
原文:https://blog.csdn.net/georgeye/article/details/85318802
以前项目打包一直是用的 maven,最近新开一个项目,使用的是spring boot 2.11 + gradle 4.10.3,在打包的时候分离lib折腾了好几天,网上找了很多方法都不成功,老是卡在 configurations.compile 这里,总是获取不到正确的jar包路径。最后上google终于找到解决办法,总结整理后简单又好用,特此记录如下:
// 清除现有的lib目录
task clearJar(type: Delete) {
delete "$buildDir\\libs\\lib"
}
// 将依赖包复制到lib目录
task copyJar(type: Copy, dependsOn: 'clearJar') {
from configurations.compileClasspath
into "$buildDir\\libs\\lib"
}
bootJar {
// 例外所有的jar
excludes = ["*.jar"]
// lib目录的清除和复制任务
dependsOn clearJar
dependsOn copyJar
// 指定依赖包的路径
manifest {
attributes "Manifest-Version": 1.0,
'Class-Path': configurations.compileClasspath.files.collect { "lib/$it.name" }.join(' ')
}
}
运行的时候也不需要指定 java.ext.dir
或 loader.path
了,直接-jar运行就可以了。
java -jar test-0.0.1-SNAPSHOT.jar
微酷实践:
但微酷加到项目中,运行出错:
A problem occurred evaluating project ':weiku-web'.
Cannot change dependencies of configuration ':weiku-web:compile' after it has been included in dependency resolution.
解决:
找到这个文章:
于是我尝试把我的 dependencies
放到了 bootJar{}
之前,竟然真的好了。
最终微酷整理的 build.gradle 文件:
buildscript {
ext {
springBootVersion = '2.1.4.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
plugins {
id "io.spring.dependency-management" version "1.0.7.RELEASE"
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
group 'co.weiku'
version '1.0.0'
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile group: 'com.microsoft.sqlserver', name: 'mssql-jdbc', version: '7.2.2.jre8'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '2.1.4.RELEASE'
compile group: 'org.apache.commons', name: 'commons-pool2', version: '2.6.2'
compile group: 'com.alibaba', name: 'druid', version: '1.1.16'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-jdbc', version: '2.1.4.RELEASE'
compile group: 'org.mybatis.spring.boot', name: 'mybatis-spring-boot-starter', version: '2.0.1'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf', version: '2.1.4.RELEASE'
compile group: 'org.apache.shiro', name: 'shiro-spring', version: '1.4.0'
compile group: 'org.springframework.boot', name: 'spring-boot-devtools', version: '2.1.4.RELEASE'
compile project(':weiku-framework')
compile project(':weiku-common')
}
// 清除lib
task myClearLib(type: Delete) {
delete "$buildDir/libs/lib"
}
// 拷贝lib
task myCopyLib(type: Copy) {
into "$buildDir/libs/lib"
from configurations.runtime
}
bootJar {
//mainClassName = 'co.weiku.WeikuApplication'
baseName = 'weiku-web'
version = '1.0.0'
classifier = 'boot'
excludes = ["*.jar"]
// lib目录的清除和复制任务
dependsOn myClearLib
dependsOn myCopyLib
// 指定依赖包的路径,运行时不再需要指定 java.ext.dir 或 loader.path 参数。
manifest {
attributes "Manifest-Version": 1.0,
'Class-Path': configurations.runtime.files.collect { "lib/$it.name" }.join(' ')
}
}
这样运行 bootJar 任务后就会打包 thinJar 完成,并将依赖的 jar 包放在 lib 子目录下,运行测试成功。
完美收工。
终于搞定了,不错!