springboot作为目前主流的java开发框架,因为便捷和易上手的特性,深受开发者欢迎。springboot默认以jar包形式,通过java -jar
指令运行
但这样的启动方式实际上不是很友好,我们常常看到各类组建通过bin
目录下的start.sh
脚本进行启动,我们可以在.sh
脚本中书写自定义各类启动参数。这样的方式更加友好,那么我们的springboot项目可不可以打包成这样的形式启动呢?
当然可以,今天我们就来看如何实现
1. assembly-plugin插件assembly
是一个maven插件,专门用于maven项目打包。它的作用就是可以将项目打包成一个可以通过脚本文件启动的项目包
其打包后的项目文件结构如下:
bin 项目启动/停止/重启等脚本文件目录 conf 配置文件目录 lib 依赖包目录
assembly插件需要配置一个.xml
配置文件来指定打包设置。该文件常见的标签配置如下:
- id: 标识符,添加到打包文件名称的后缀符 如果设置为
${project.version}
则会将项目版本号添加到打包文件名中 - formats:打包格式,支持zip、tar、tar.gz (or tgz)、tar.bz2 (or tbz2)、jar、dir、war等格式,可以同时指定多种打包形式,通过
format
标签指定
tar.gz
jar
- includeBaseDirectory 是否包含打包层目录,当设置false时,所有文件直接打包到根目录下;为true时,打包到
${artifactId}
目录下 - dependencySets 设置项目依赖包打包的目录
- fileSets 设置要包含的文件集,可以定义多个fileSet,单个fileSet标签中又包含如下子标签 directory:要打包的文件夹 outputDirectory:打包出来的文件夹,比如为bin,则表示将directory文件夹下的文本打包到bin目录下 includes: 指定要包含(include)或排除(exclude)的打包文件 fileMode:指定文件权限,权限的指定参考linux系统文件权限,比较常用的0755,0644。我们在文末单独讲解 directoryMode:指定目录权限
src/main/assembly/bin
bin
start.sh
stop.sh
0755
2. 实操
1、首先我们先创建一个springboot项目assembly-plugin-demo
,并且引入spring web
依赖,模拟一个web项目
org.springframework.boot
spring-boot-starter-web
2、创建一个HelloController,并书写一个简单的接口,用于模拟接口
/**
* @author benjamin_5
* @Description
* @date 2022/8/27
*/
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello~~";
}
}
3、在项目src/main下创建bin目录,并声明启动/停止/重启脚本
启动脚本 start.sh
#!/usr/bin/env bash
#source $(dirname $0)/../../env.sh
# 进入当前文件目录
cd `dirname $0`
# 返回上一级
cd ..
# jar包名称
#SERVER_JAR="$SERVER_NAME-$PROJECT_VERSION.jar"
SERVER_JAR="assembly_plugin_demo-0.0.1-SNAPSHOT.jar"
BASE_DIR=`pwd`
# 获取java路径
if [ "$JAVA_HOME" != "" ]; then
JAVA="$JAVA_HOME/bin/java"
else
JAVA=java
fi
# 指定日志输出路径
LOGS_DIR=""
if [ -n "$LOGS_FILE" ]; then
LOGS_DIR=`dirname $LOGS_FILE`
else
LOGS_DIR=$BASE_DIR/logs
fi
if [ ! -d $LOGS_DIR ]; then
mkdir $LOGS_DIR
fi
STDOUT_FILE=$LOGS_DIR/stdout.log
# 设置jvm参数
JAVA_OPTS="-server -Xms1G -Xmx2G -Xmn256m -Xss1m \
-XX:SurvivorRatio=4 -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection \
-XX:CMSInitiatingOccupancyFraction=60 -XX:+PrintGCDateStamps \
-XX:+PrintGCDetails -Xloggc:$LOGS_DIR/gc.log"
# 如果项目已经启动则之前停止项目
echo -n "Starting server ..."
PID=$(ps -ef | grep $SERVER_JAR | grep -v grep |awk '{print $2}')
if [ -z "$PID" ]; then
echo Application is already stopped
else
echo kill $PID
kill -9 $PID
fi
# 以指定参数启动项目
nohup $JAVA $JAVA_OPTS $JAVA_DEBUG_OPT -jar lib/$SERVER_JAR > $STDOUT_FILE 2>&1 &
if [ $? -eq 0 ];then
# echo -n $! > "$PIDFILE"
if [ $? -eq 0 ]
then
sleep 1
echo STARTED
else
echo FAILED TO WRITE PID
exit 1
fi
else
echo SERVER DID NOT START
exit 1
fi
PID_NOW=`ps -ef | grep java | grep -v grep | grep "$SERVER_JAR" | awk '{print $2}'`
# 打印参数
echo "进程ID: $PID_NOW"
echo "输出日志:$STDOUT_FILE"
停止脚本 stop.sh
#!/usr/bin/env bash
# jar包名称
SERVER_JAR="assembly_plugin_demo-0.0.1-SNAPSHOT.jar"
# 停止项目
echo -n "Stopping server ..."
PID=$(ps -ef | grep $SERVER_JAR | grep -v grep |awk '{print $2}')
if [ -z "$PID" ]; then
echo Application is already stopped
else
echo kill $PID
kill -9 $PID
fi
exit 0
4、然后我们在项目的src/main
目录下创建一个assembly
目录,并且创建一个assembly插件的配置文件assembly.xml
我们需要在assembly.xml
配置文件中申明bin
,conf
,lib
三个路径
assembly
tar.gz
true
src/main/assembly/bin
bin
0755
src/main/resources
conf
0644
lib
5、其次我们在pom.xml
中引入assembly-plugin
插件
maven-assembly-plugin
src/main/assembly/assembly.xml
make-assembly
package
single
6、重新加载pom
文件,下载assembly插件
7、执行打包指令
出现BUILD SUCCESS
说明打包成功
8、打包文件输出在target
目录下,如果有指定输出目录,则在输出目录中查找
9、我们将打包出来的’.tar.gz’压缩包文件解压,可以看到其结构就是我们上述指定的bin、conf、lib目录
10、进入bin目录,执行start.sh
指令启动项目
./start.sh
11、我们已经将输出内容指定到logs目录下的stdout.log,我们可以通过该日志查看启动明细
如果启动失败,也可以通过该日志文件查看报错明细。同时也指定了gc日志gc.log
,可以通过该文件查看gc详情
12、从上述日志看启动成功了,我们通过访问之前定义的接口来测试
可以看到正常输出了接口数据,证明我们打包并启动成功了
如上,我们的springboot就成功打包成了bin目录启动形式。
2.1 优化【未完善】上述打包程序并不是最佳的,因为我们的项目的jar包名称是在start.sh脚本中写死的,如果版本升级,或者复用到其他模块中时,就需要修改脚本了,非常的不方便
所以我们做出优化,希望能够自动获取这个jar包名称
可以看到我们的jar包名称是:assembly_plugin_demo-0.0.1-SNAPSHOT.jar
这个名称由两部分组成:服务名,版本号
于是我们就要想办法获取这两个属性
1、首先服务名直接在application.properties配置文件中通过spring.application.name
属性获取。
2、配置文件是会一起打包到conf文件夹下的,但我们怎么在shell脚本中获取配置文件中的指定配置项呢?
答案是可以通过sed指令,如下所示
PROJECT_VERSION=`sed '/^project.version=/!d;s/.*=//' conf/application.properties`
3、其次就是版本号,我们知道版本号是定义在pom.xml文件中的,打包后是没有pom文件的,那么我们的思路就是在application.properties配置文件中获取到版本号
这里就要考验大家的springboot知识积累了,如何在配置文件中获取到pom属性呢?
4、首先我们要在pom.xml文件中,添加resource标签,设置filtering=true,让配置文件可以读取pom属性
src/main/resources
true
5、设置完成后记得重新加载pom文件!!!
6、然后在application,properties配置文件中通过@xxx@
获取pom属性"project.version"
application.version=@project.version@
7、在start.sh脚本中再声明版本号和jar包名变量
PROJECT_VERSION=`sed '/^application.version=/!d;s/.*=//' conf/application.properties`
SERVER_JAR="$SERVER_NAME-$PROJECT_VERSION.jar"
8、重新打包即可,stop.sh脚本中处理类似
9、这里存在一个问题尚未解决,上述说的@xxx@的方式读取pom属性,在idea中打包,可以看到本身的打包插件已经加载了pom中的属性,如下图
但是assembly插件打包的conf目录中的配置文件中却没能加载pom的值,初步怀疑是加载顺序的问题,仍在研究中,这里也抛出问题点,大家一起讨论解决。有思路的同学,可以留言讨论
演示代码见文末
3. 补充内容 fileMode/directoryMode权限assembly中的权限表示与linux系统一致,通过4位数值表示权限
其中第一位0表示采用十进制,第二位表示用户权限,第三位组用户权限,第四位表示其他用户权限,而十进制表示的权限等级如下:
十进制linux权限说明0—无任何权限1–x执行权限2–w可写权限3-wx可写、执行权限4r–只读权限5r-x可读、可执行权限6rw-读写权限7rwx全部权限则0755表示用户具有读/写/执行权限,组用户和其它用户具有读写权限;0644表示用户具有读写权限,组用户和其他用户具有只读权限
4. 演示源码git地址