0.
0.1. 疑问
- 开发出的 Spring Boot 应用,该如何打包?
- 项目不可避免要引入三方依赖包,这种场景下该如何打包呢?
- Spring Boot 应用编译打包后,该如何发布呢?
本次重点探讨 Spring Boot 应用的打包和发布。
1. 创建项目(ToyApp)
为了演示需要,基于 Spring Boot 创建一个 WEB 项目 ToyApp。
稍微注意一点:本次引入了一个三方依赖包(idgenerator-spring-boot-starter 是自定义的一个序列号生成器 starter)。
准备好环境,下面一起瞅瞅引入了三方依赖的 Spring Boot 该如何打包?
2. 编译打包
采用 IDEA 集成的 Maven 环境来对 Spring Boot 项目编译打包,可谓是超级 easy。
此时,通过反编译工具来瞅瞅编译打包成功之后的 ToyApp-0.0.1-SNAPSHOT.jar 里面的东西是否缺失,尤其是引入的三方依赖 jar 包。
通过反编译,发现引入的三方依赖 jar 包,没有打进去,咋回事儿?
由于 scope 为 system 的包 maven 默认是不打包进去的,所以解决方案就很简单:只需在 pom.xml 中指定一下 includeSystemScope 就可以。
代码语言:javascript复制<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <includeSystemScope>true</includeSystemScope> </configuration> </plugin> </plugins></build>
再次编译打包,然后对打成的 jar 包反编译观察一二。
至此,Spring Boot 项目打包就完成了,如果经历过传统的项目开发,相对传统的打包方式,你会知道着实简单不少。
3. 应用发布
3.1 一行命令的演化
运行 Spring Boot 打包之后的 jar 方式也简单,只需一行命令就行。
代码语言:javascript复制java -jar /Users/caicai/ToyApp-0.0.1-SNAPSHOT.jar
执行效果:
此时,服务是启动了,但是不能关闭这个窗口,一旦关闭服务就停止了,不得不考虑后台运行,并且还想看日志,所以这一行命令演变成了这个鸟样。
代码语言:javascript复制java -jar /Users/caicai/ToyApp-0.0.1-SNAPSHOT.jar > ToyApp.out &
执行启动时,效果如下,然后就可以轻松看日志输出了。
如果项目组中你既是研发又充当运维的角色,到这基本就完事儿了,因为相信通过熟练操作,会形成肌肉反应,你肯定能记住这一行命令。
不过,若是分工明确,生产权限隔离的话,一般都是运维同事来操作发布,所以还得想办法让运维同事省力,不得不考虑脚本化。
3.2 两个脚本
每个项目组的风格不同,而我习惯采取如下目录结构进行管理。
代码语言:javascript复制.├── bin(脚本存放目录)│ ├── start.sh│ └── stop.sh├── lib(jar包存放目录)│ └── ToyApp-0.0.1-SNAPSHOT.jar└── logs(日志目录) ├── toyapp.gc.log └── toyapp.out
首先创建项目目录例如 toyapp,然后分别创建 bin、lib、logs 目录;把 ToyApp-0.0.1-SNAPSHOT.jar 拷贝至 lib 目录下;bin 目录主要存放脚本。
- 启动脚本(start.sh)
在 bin 目录下,创建 start.sh,应用启动脚本。
代码语言:javascript复制#!/bin/bash
#配置 Java 环境变量export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_251.jdk/Contents/Homeexport PATH=$JAVA_HOME/bin:$PATH#定义应用名App_Name=toyapp#定义应用所在目录App_Path=/Users/caicai/${App_Name}#定义可执行文件的路径JAR_PATH=${App_Path}/lib/ToyApp-0.0.1-SNAPSHOT.jar#jvm启动参数JAVA_OPTS="-Duser.timezone=GMT 8 -server -Xms4096m -Xmx4096m -XX:MaxMetaspaceSize=256m -Xloggc:${App_Path}/logs/${App_Name}.gc.log -XX: UseConcMarkSweepGC -XX: UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5 -XX: PrintGC -XX: PrintGCTimeStamps -XX: PrintGCDetails -XX: PrintGCApplicationStoppedTime"#启动JAVA进程函数CURRENT_COUNT=`ps -ef|grep java |grep ${App_Name} |grep -vc grep`if [ $CURRENT_COUNT -eq 0 ]then Log_Name=$(echo ${App_Name}|awk -F"-" '{ print $NF }') nohup java -Dfunc_type=${App_Name} $JAVA_OPTS -Dfile.encoding=utf-8 -jar $JAR_PATH > ${App_Path}/logs/${Log_Name}.out 2>&1 & PROCESS_ID=`ps -ef | grep "${App_Name}" |grep -v grep | awk '{ print $2 }'` echo "