java项目打包最佳实践

2020-09-09  本文已影响0人  Nisus_Liu

摸索了一晚上外加多年经验终于有了这个最佳实践.

环境

需求

配置参考

pom.xml

<profiles>
        <profile>
            <!-- 本地开发环境 -->
            <id>dev</id>
            <properties>
                <profileActive>dev</profileActive>
            </properties>
            <activation>
                <activeByDefault>true</activeByDefault>
            </activation>
            <!--<build>
                &lt;!&ndash;Idea运行时, 需要借助这个配置build指定的配置文件. 打包时排除了. (缺点是打包dev时配置文件不会分离)&ndash;&gt;
                <resources>
                    <resource>
                        <directory>src/main/resources</directory>
                        <filtering>true</filtering>
                    </resource>
                </resources>
            </build>-->
        </profile>
        <profile>
            <!-- 测试环境 -->
            <id>test</id>
            <properties>
                <profileActive>test</profileActive>
            </properties>
        </profile>
        <profile>
            <!-- 生产环境 -->
            <id>prod</id>
            <properties>
                <profileActive>prod</profileActive>
            </properties>
        </profile>

    </profiles>


    <build>
        <!--例: 让logback.xml中解析pom.xml的属性变量-->
        <pluginManagement>
            <plugins>
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <configuration>
                        <encoding>utf-8</encoding>
                        <useDefaultDelimiters>true</useDefaultDelimiters>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
        <finalName>${project.artifactId}-${project.version}</finalName>
        <resources>
            <!--让xml等资源文件在maven这里过一手, 就可以解析里面的属性值引用了 `${xxx}`-->
            <resource>
                <directory>src/main/resources</directory>
                <!--<includes>-->
                <!--    <include>**/*.xml</include>-->
                <!--</includes>-->
                <!--<excludes>
                    &lt;!&ndash;排除 resources 下所有(assembly将其放到根目录下)&ndash;&gt;
                    <exclude>**/*</exclude>
                    &lt;!&ndash;<exclude>**/*.properties</exclude>&ndash;&gt;
                    &lt;!&ndash;<exclude>**/*.yml</exclude>&ndash;&gt;
                </excludes>-->
                <filtering>true</filtering>
            </resource>
        </resources>
        <plugins>
            <!--<plugin>-->
            <!--    <groupId>org.springframework.boot</groupId>-->
            <!--    <artifactId>spring-boot-maven-plugin</artifactId>-->
            <!--</plugin>-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <!--<version>2.2-beta-5</version>-->
                <configuration>
                    <descriptors>
                        <descriptor>src/main/resources/assembly.xml</descriptor>
                    </descriptors>
                </configuration>
                <executions>
                    <execution>
                        <id>make-my-jar-with-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                        <configuration>
                            <finalName>${project.artifactId}</finalName>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <!--不打包资源文件 !推荐, 这样可以实现打包和Ide运行分开, 打包排除资源文件, Ide运行包含, 保证本地运行成功. 优于 resource 中配置排除.
                    但需要仔细, 避免将不能排除的文件排除了-->
                    <excludes>
                        <exclude>*.xml</exclude>
                        <exclude>*.yml</exclude>
                        <exclude>*.properties</exclude>
                        <exclude>*.sh</exclude>
                        <exclude>public</exclude>
                        <exclude>conf</exclude>
                    </excludes>
                    <archive>
                        <manifest>
                            <!--指定入口类-->
                            <mainClass>com.gx.app.GxAppApplication</mainClass>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                            <!-- manifest是否含时间戳  jar包不包含唯一版本标识 -->
                            <useUniqueVersions>false</useUniqueVersions>
                        </manifest>
                        <manifestEntries>
                            <!--MANIFEST.MF 中 Class-Path 有当前目录  ! devtools 不能打进依赖, 否则不停的重启 !
                            当前目录加入classpath便于使用当前目录下的配置文件, 如 logback(就不需要脚本中指定配置文件了)-->
                            <Class-Path>./</Class-Path>
                            <!--    <implementation-version>${project.version}</implementation-version>-->
                            <!--    <implementation-build>${buildNumber}</implementation-build>-->
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

assembly.xml

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
    <id>assembly</id>
    <formats>
        <format>dir</format>
    </formats>
    <fileSets>
        <fileSet>
            <directory>${project.basedir}</directory>
            <outputDirectory>/</outputDirectory>
            <filtered>true</filtered>
            <includes>
                <include>application.yml</include>
                <include>application-${profileActive}.yml</include>
                <include>*.xml</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>${project.build.directory}/site</directory>
            <outputDirectory>docs</outputDirectory>
        </fileSet>
        <!--     <fileSet> -->
        <!--       <directory>${project.basedir}/src/main/resources</directory> -->
        <!--       <outputDirectory>/</outputDirectory> -->
        <!--       <filtered>true</filtered> -->
        <!--       <fileMode>0755</fileMode> -->
        <!--       <includes> -->
        <!--         <include>*.properties</include> -->
        <!--       </includes> -->
        <!--     </fileSet> -->
        <fileSet>
            <directory>${project.basedir}/src/main/resources</directory>
            <outputDirectory>/</outputDirectory>
            <filtered>true</filtered>
            <fileMode>0775</fileMode>
            <includes>
                <!--将资源全部移到根目录下-->
                <include>**/*</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>${project.basedir}/src/main/resources/conf/${profileActive}</directory>
            <outputDirectory>/</outputDirectory>
            <filtered>true</filtered>
            <fileMode>0755</fileMode>
            <includes>
                <include>*.properties</include>
            </includes>
        </fileSet>
    </fileSets>
    <dependencySets>
        <dependencySet>
            <outputDirectory>/lib</outputDirectory>
            <outputFileNameMapping>
                ${artifact.artifactId}-${artifact.baseVersion}${dashClassifier?}.${artifact.extension}
            </outputFileNameMapping>
            <useProjectArtifact>true</useProjectArtifact>
            <scope>runtime</scope>
            <excludes>
                <exclude>${project.groupId}:${project.artifactId}:*</exclude>
            </excludes>
        </dependencySet>
        <dependencySet>
            <scope>provided</scope>
            <outputDirectory>/</outputDirectory>
            <includes>
                <include>${project.groupId}:${project.artifactId}:*</include>
            </includes>
        </dependencySet>
    </dependencySets>
</assembly>

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- Reference Manual http://logback.qos.ch/manual/index.html -->
<configuration>
    <!-- 修改部分 如果有job需要监控,单独修改 -->
    <property name="LOG_HOME" value="./logs" />

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder charset="UTF-8">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 所有日志 -->
    <appender name="RollingFile"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/${project.artifactId}.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/rolling/${project.artifactId}_%d{yyyy-MM-dd}.%i.log.zip
            </fileNamePattern>
            <!-- 保存多少天 -->
             <maxHistory>30</maxHistory>
            <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MB</maxFileSize>
            </timeBasedFileNamingAndTriggeringPolicy>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>

    </appender>

    <!-- ###################### SQL日志监控 ###################### -->
    <appender name="SqlStatistics"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/sql/${project.artifactId}_sql_%d{yyyy-MM-dd}.log
            </fileNamePattern>
            <!-- <maxHistory>3</maxHistory> -->
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>




    <logger name="java.sql.Connection" level="INFO">
        <appender-ref ref="SqlStatistics" />
    </logger>
    <logger name="java.sql.Statement" level="INFO">
        <appender-ref ref="SqlStatistics" />
    </logger>
    <logger name="java.sql.PreparedStatement" level="INFO">
        <appender-ref ref="SqlStatistics" />
    </logger>

    <!-- ###################### SQL日志监控 ###################### -->



    <!-- ###################### DAO日志监控 ###################### -->
    <appender name="DaoStatistics"
        class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/dao/${project.artifactId}_dao_%d{yyyy-MM-dd}.log
            </fileNamePattern>
            <!-- <maxHistory>3</maxHistory> -->
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>


<!--    <logger name="**.dao.**" level="INFO"> -->
    <logger name="com.jfbank.fincloud.loan.cif.biz.core.dao" level="DEBUG">
        <appender-ref ref="DaoStatistics" />
    </logger>

    <!-- ###################### SQL日志监控 ###################### -->

    <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/error/${project.artifactId}_error.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_HOME}/error/${project.artifactId}_error_%d{yyyy-MM-dd}.log
            </fileNamePattern>
            <!-- <maxHistory>60</maxHistory> -->
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>


    <!-- 测试环境+开发环境. 多个使用逗号隔开. -->
    <springProfile name="dev">
        <root level="DEBUG">
            <appender-ref ref="STDOUT" />
             <appender-ref ref="RollingFile" />
             <appender-ref ref="errorFile" />
        </root>
    </springProfile>

    <springProfile name="test">
        <root level="DEBUG">
            <!-- <appender-ref ref="STDOUT" /> -->
            <appender-ref ref="RollingFile" />
            <appender-ref ref="errorFile" />
        </root>
    </springProfile>

    <!-- 生产环境. -->
    <springProfile name="prod">
        <root level="INFO">
            <!-- <appender-ref ref="STDOUT" /> -->
            <appender-ref ref="RollingFile" />
            <appender-ref ref="errorFile" />
        </root>
    </springProfile>

</configuration>

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
    fi

    echo -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 1

        COUNT=`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!"
        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 --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

目录结构

image.png

打包产物
配置文件, 静态资源都在根下.

image.png

主程序执行jar包结构
没有配置文件了.

image.png

坑点

如何配置不当:

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

猜你喜欢

热点阅读