java项目打包最佳实践

摸索了一晚上外加多年经验终于有了这个最佳实践.
环境
  • assembly
  • springboot
  • idea
  • java8
  • logback
需求
  • jar包要可执行: java -jar jar_name.
  • 依赖分离: 主业务代码独立成可执行jar, 其他所有依赖放到./lib下.
  • 配置文件分离: application.yml, logback.xml等配置文件不在jar内, 而是在项目根目录下. 这样方便中途查看和修改配置.
  • 分环境打包: 我这里分了三个环境dev,test,prod.
  • 脚本启动,停止,重启应用.
配置参考 pom.xml
dev【java项目打包最佳实践】dev truetesttest prodprod maven-resources-plugin utf-8 true ${project.artifactId}-${project.version} src/main/resourcestrue org.apache.maven.plugins maven-compiler-plugin 1.8 1.8 UTF-8 org.apache.maven.plugins maven-assembly-plugin src/main/resources/assembly.xml make-my-jar-with-dependenciespackage single ${project.artifactId} org.apache.maven.plugins maven-jar-plugin *.xml *.yml *.properties *.sh public conf com.gx.app.GxAppApplication true lib/ truefalse ./

assembly.xml
assembly dir ${project.basedir} / true application.yml application-${profileActive}.yml *.xml ${project.build.directory}/site docs ${project.basedir}/src/main/resources / true 0775 **/* ${project.basedir}/src/main/resources/conf/${profileActive} / true 0755 *.properties /lib ${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension} true runtime ${project.groupId}:${project.artifactId}:* provided / ${project.groupId}:${project.artifactId}:*

logback.xml
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ${LOG_HOME}/${project.artifactId}.log ${LOG_HOME}/rolling/${project.artifactId}_%d{yyyy-MM-dd}.%i.log.zip 30 100MB %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ${LOG_HOME}/sql/${project.artifactId}_sql_%d{yyyy-MM-dd}.log %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ${LOG_HOME}/dao/${project.artifactId}_dao_%d{yyyy-MM-dd}.log %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ${LOG_HOME}/error/${project.artifactId}_error.log ${LOG_HOME}/error/${project.artifactId}_error_%d{yyyy-MM-dd}.log %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n ERROR ACCEPT DENY

application.yml
spring: application: name: GxApp profiles: active: @profileActive@logging: level: root: info com.gx: debug

启动脚本
#!/bin/bash#此脚本为Linux下启动java程序的通用脚本。(包含启动,停止,重启) #cd 进入脚本执行的bin目录,sh run.sh start(启动) | stop(停止)| restart(重启) ##bin目前路径以及相关目录路径 cd `dirname $0` BIN_DIR=`pwd` DEPLOY_DIR=`pwd` #LOG_BACK=-Dlogback.configurationFile=$DEPLOY_DIR/logback.xml JAVA_DEBUG_OPTS=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n " JAVA_JMX_OPTS=" -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false-Djava.rmi.server.hostname=$ip " #拼接全路径jar包名, 为了精确匹配jar包, 能确保准确定位pid JAR_NAME=$BIN_DIR/`ls ${project.artifactId}*.jar` cmd=$2 echo "$cmd"start(){ echo "DEPLOY_DIR=$DEPLOY_DIR" PIDS=`ps--no-heading -C java -f --width 1000 | grep "$DEPLOY_DIR" |awk '{print $2}'` if [ -n "$PIDS" ]; then echo "ERROR: The $SERVER_NAME already started!" echo "PID: $PIDS" exit 1 fiecho -e "Starting the $JAR_NAME ...\c" nohup java -Xms256M -Xmx1024M -XX:PermSize=128M -XX:+HeapDumpOnOutOfMemoryError -verbose:gc -XX:+PrintGCDetails -Xloggc:./gc.log-XX:+PrintGCDateStamps -jar $JAR_NAME > /dev/null 2>&1 & COUNT=0 while [ $COUNT -lt 1 ]; do echo -e ".\c" sleep 1COUNT=`ps--no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}' | wc -l`if [ $COUNT -gt 0 ]; then break fi done echo "start OK!" PIDS=`ps--no-heading -C java -f --width 1000 | grep "$JAR_NAME" | awk '{print $2}'` echo "PID: $PIDS" echo $PIDS > .pid}stop(){PIDS=`ps--no-heading -C java -f --width 1000 | grep "$JAR_NAME" |awk '{print $2}'` if [ -z "$PIDS" ]; then echo "ERROR: The $SERVER_NAME does not started!" fiecho -e "Stopping the $SERVER_NAME ...\c" for PID in $PIDS ; do kill $PID > /dev/null 2>&1 doneCOUNT=0 while [ $COUNT -lt 1 ]; do echo -e ".\c" sleep 1 COUNT=1 for PID in $PIDS ; do PID_EXIST=`ps --no-heading -p $PID` if [ -n "$PID_EXIST" ]; then COUNT=0 break fi done done echo "stop OK!" echo "PID: $PIDS" rm .pid}case $1 in start) start; ; ; stop) stop; ; ; restart) echo "############ Application of '"$JAR_NAME"' restarting....############" stop; sleep 2 start; ; ; *) echo "Usage: startup.sh {start|stop|restart}" ; ; esac exit 0

目录结构
java项目打包最佳实践
文章图片
image.png 打包产物
配置文件, 静态资源都在根下.

java项目打包最佳实践
文章图片
image.png
主程序执行jar包结构
没有配置文件了.

java项目打包最佳实践
文章图片
image.png
坑点 如何配置不当:
  1. 打包时配置文件能够隔离. 但IDE里本地运行时, 找不到配置文件, 因为配置文件被排除了.
  2. spring 的devtools 一定要设置成 scope provided, 让它不参与打包. 这样可以避免在设置当前目录加入了classpath, 而日志文件又写入当亲目录内时, 应用不停的重启. (这是因为检测到了classpath内内容变化就会热部署, 就会重启).
  3. 找不到 logback.xml 配置文件. 解决: M1: 加入当前目录到classpath; M2: 启动命令中指定文件.

    推荐阅读