SpringBoot + Maven 打包部署示例
前言
在现代企业级应用开发中,快速构建、高效部署和易于维护是至关重要的。Spring Boot 以其简洁的配置和强大的自动化功能,极大地简化了这一过程。本文将介绍如何使用 SpringBoot 结合 Maven 进行项目构建和打包。示例最终会构建出目录结构为 lib、conf、bin、logs 的 zip 包,内含着项目运行所需要的 jar 包、配置文件、启动脚本。
配置
项目结构:
-- src
-- -- bin
-- -- -- start.sh
-- -- -- stop.sh
-- -- -- run.sh
-- -- -- restart.sh
-- -- main
-- -- -- assembly
-- -- -- -- assembly.xml
-- -- -- java
-- -- -- resources
-- pom.xml
pom.xml 配置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
</parent>
<groupId>cn.phixlin</groupId>
<artifactId>package-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- 依赖包打入 lib 下 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.1</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>target/lib</outputDirectory>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<overWriteIfNewer>true</overWriteIfNewer>
<includeScope>compile</includeScope>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<!-- 只执行一次 -->
<goal>single</goal>
</goals>
<configuration>
<appendAssemblyId>false</appendAssemblyId>
<archive>
<manifest>
<!--jar 包的入口 main 方法 -->
<mainClass>cn.phixlin.PackageApplication</mainClass>
</manifest>
</archive>
<!-- 生成的 jar 包的名称前缀 -->
<finalName>${project.build.finalName}</finalName>
<descriptors>
<!-- assembly 插件的配置文件路径 -->
<descriptor>src/main/assembly/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
assembly.xml 配置:
<assembly>
<!-- 可自定义,这里指定的是项目环境 -->
<id>prod</id>
<formats>
<!-- 打包的类型,如果有 N 个,将会打 N 个类型的包 -->
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<!--
0755-> 即用户具有读 / 写 / 执行权限,组用户和其它用户具有读写权限;
0644-> 即用户具有读写权限,组用户和其它用户具有只读权限;
-->
<!-- 将 src/bin 目录下的所有文件输出到打包后的 bin 目录中 -->
<fileSet>
<directory>${basedir}/src/bin</directory>
<outputDirectory>bin</outputDirectory>
<fileMode>0755</fileMode>
<includes>
<include>**.sh</include>
</includes>
</fileSet>
<!-- 指定输出 target/classes 中的配置文件到 conf 目录中 (只复制配置文件, 不复制 xml 文件,xml 文件已在启动的 jar 中存在) -->
<fileSet>
<directory>${basedir}/target/classes</directory>
<outputDirectory>conf</outputDirectory>
<fileMode>0644</fileMode>
<includes>
<include>application.yml</include>
<include>application-${env.devMode}.yml</include>
<include>bootstrap.yml</include>
<include>*.properties</include>
</includes>
</fileSet>
<!-- 将第三方依赖打包到 lib 目录中 -->
<fileSet>
<directory>${basedir}/target/lib</directory>
<outputDirectory>lib</outputDirectory>
<fileMode>0644</fileMode>
</fileSet>
<!-- 将项目启动 jar 打包到 lib 目录中 -->
<fileSet>
<directory>${basedir}/target</directory>
<outputDirectory>lib</outputDirectory>
<fileMode>0644</fileMode>
<includes>
<include>${project.build.finalName}.jar</include>
</includes>
</fileSet>
<!-- 包含根目录下的文件 -->
<fileSet>
<directory>${basedir}</directory>
<includes>
<include>NOTICE</include>
<include>LICENSE</include>
<include>*.md</include>
</includes>
</fileSet>
</fileSets>
</assembly>
bin 脚本
start.sh
#!/bin/bash
cd `dirname $0`
BIN_DIR=`pwd`
cd ..
DEPLOY_DIR=`pwd`
CONF_DIR=$DEPLOY_DIR/conf/
APPLICATION="package-demo"
APPLICATION_JAR="${APPLICATION}.jar"
SERVER_NAME=""
SERVER_PORT=""
SERVER_HOST=""
SERVER_PROTOCOL=""
VM_ARGS_PERM_SIZE='PermSize'
VM_ARGS_METASPACE_SIZE='MetaspaceSize'
JAVA_8_VERSION="180"
if [ -z "$SERVER_HOST" ]; then
SERVER_HOST='127.0.0.1'
fi
if [ -z "$SERVER_NAME" ]; then
SERVER_NAME=$APPLICATION
fi
PIDS=`ps -ef | grep java | grep -v grep | grep "$CONF_DIR" |awk '{print $2}'`
if [ -n "$PIDS" ]; then
echo "ERROR: The $SERVER_NAME already started!"
echo "PID: $PIDS"
exit 1
fi
if [ -n "$SERVER_PORT" ]; then
SERVER_PORT_COUNT=`netstat -tln | grep $SERVER_PORT | wc -l`
if [ $SERVER_PORT_COUNT -gt 0 ]; then
echo "ERROR: The $SERVER_NAME port $SERVER_PORT already used!"
exit 1
fi
fi
LOGS_DIR=$DEPLOY_DIR/logs
if [ ! -d $LOGS_DIR ]; then
mkdir $LOGS_DIR
fi
STDOUT_FILE=$LOGS_DIR/stdout.log
LIB_DIR=$DEPLOY_DIR/lib
LIB_JARS=`ls $LIB_DIR|grep .jar|awk '{print"'$LIB_DIR'/"$0}'|tr "\n" ":"`
JAVA_OPTS="-Djava.awt.headless=true -Djava.net.preferIPv4Stack=true"
JAVA_DEBUG_OPTS=""
if [ "$1" = "debug" ]; then
JAVA_DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"
fi
JAVA_JMX_OPTS=""
if [ "$1" = "jmx" ]; then
JAVA_JMX_OPTS="-Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
fi
JAVA_MEM_OPTS=""
# set jvm args by different java version
JAVA_VERSION=`java -fullversion 2>&1 | awk -F[\"\.]'{print $2$3$4}' |awk -F"_" '{print $1}'`
echo $JAVA_VERSION
VM_ARGS=${VM_ARGS_PERM_SIZE}
# if you use dubbo in java 9
TEMP_VERSION=$(echo ${JAVA_VERSION} | grep "+")
if [[ "$TEMP_VERSION" != "" ]]; then
JAVA_VERSION=$(echo ${JAVA_VERSION} | awk -F"+" '{print $1}')
fi
# compare java version
if [ "${JAVA_VERSION}" -ge ${JAVA_8_VERSION} ]; then
VM_ARGS=${VM_ARGS_METASPACE_SIZE}
fi
# MaxInlineLevel=15 is the default since JDK 14 and can be removed once older JDKs are no longer supported
BITS=`java -version 2>&1 | grep -i 64-bit`
JAVA_MEM_OPTS=" -server -Xmx4g -Xms2g -XX:${VM_ARGS}=256m -Xss256k -XX:+DisableExplicitGC -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+PrintGCDetails "
if [ -n "$BITS" ]; then
JAVA_MEM_OPTS=" -server -Xmx4g -Xms2g -Xmn256m -XX:${VM_ARGS}=128m -Xss512k -XX:LargePageSizeInBytes=128m -XX:+DisableExplicitGC -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8 -XX:G1ReservePercent=15 "
else
JAVA_MEM_OPTS=" -server -Xms1g -Xmx2g -XX:${VM_ARGS}=128m -XX:SurvivorRatio=2 -XX:+UseParallelGC "
fi
echo -e "Starting the $SERVER_NAME ...\c"
nohup java ${JAVA_JMX_OPTS} ${JAVA_OPTS} ${JAVA_MEM_OPTS} -Xbootclasspath/a:${CONF_DIR} -jar ${DEPLOY_DIR}/lib/${APPLICATION_JAR} --spring.config.location=${CONF_DIR} -DJM.SNAPSHOT.PATH=${CONF_DIR}/nacos/download > $STDOUT_FILE 2>&1 &
COUNT=0
while [ $COUNT -lt 1 ]; do
echo -e ".\c"
sleep 1
if [ -n "$SERVER_PORT" ]; then
if [ "$SERVER_PROTOCOL" == "dubbo" ]; then
COUNT=`(sleep 1; echo -e '\n'; sleep 1; echo status; sleep 1)| telnet $SERVER_HOST $SERVER_PORT | grep -c OK`
else
COUNT=`netstat -an | grep $SERVER_PORT | wc -l`
fi
else
COUNT=`ps -ef | grep java | grep -v grep | grep "$DEPLOY_DIR" | awk '{print $2}' | wc -l`
fi
if [ $COUNT -gt 0 ]; then
break
fi
done
echo "OK!"
PIDS=`ps -ef | grep java | grep -v grep | grep "$DEPLOY_DIR" | awk '{print $2}'`
echo "PID: $PIDS"
echo "STDOUT: $STDOUT_FILE"
stop.sh
#!/bin/bash
cd `dirname $0`
BIN_DIR=`pwd`
cd ..
DEPLOY_DIR=`pwd`
CONF_DIR=$DEPLOY_DIR/conf/
SERVER_NAME="package-demo"
if [ -z "$SERVER_NAME" ]; then
SERVER_NAME=`hostname`
fi
PIDS=`ps -ef | grep java | grep -v grep | grep "$CONF_DIR" |awk '{print $2}'`
if [ -z "$PIDS" ]; then
echo "ERROR: The $SERVER_NAME does not started!"
exit 1
fi
echo -e "Stopping the $SERVER_NAME ...\c"
for PID in $PIDS ; do
kill $PID > /dev/null 2>&1
done
COUNT=0
while [ $COUNT -lt 1 ]; do
echo -e ".\c"
sleep 1
COUNT=1
for PID in $PIDS ; do
PID_EXIST=`ps -f -p $PID | grep java`
if [ -n "$PID_EXIST" ]; then
COUNT=0
break
fi
done
done
echo "OK!"
echo "PID: $PIDS"
run.sh
#!/bin/bash
cd `dirname $0`
if [ "$1" = "start" ]; then
./start.sh
else
if [ "$1" = "stop" ]; then
./stop.sh
else
if [ "$1" = "debug" ]; then
./start.sh debug
else
if [ "$1" = "restart" ]; then
./restart.sh
else
echo "ERROR: Please input argument: start or stop or debug or restart"
fi
fi
fi
fi
restart.sh
#!/bin/bash
cd `dirname $0`
./stop.sh
./start.sh
测试
- 执行 maven 打包命令:
mvn clean package -Dmaven.test.skip=true
- 上传并解压到服务器:
unzip package-demo-1.0.0.zip
- 启动项目:
cd package-demo-1.0.0 && sh bin/run.sh start
- 查看项目是否启动成功:
ps -ef | grep package-demo
后记:
- 需要根据自己的项目名和版本号修改脚本
- 需要根据项目实际修改 JVM 启动参数
License:
CC BY 4.0