文章目录

  • 系列文章目录
  • 使用maven将Java程序打包成exe文件并制作成Windows系统服务(一)之springboot打包exe并绑定jdk
  • 前言
  • 一、Apache commons-daemon下载
  • 二、项目改造并制作Windows服务
  • 三、运行并测试服务
  • 总结



前言

在上一节中我们介绍了怎么把springboot项目打包成exe文件,并绑定了自己的jdk运行时,但是我们在运行这个项目时,需要启动控制台,如果不小心操作了控制台,我们的程序也会收到影响,这是一个瑕疵。下面我们将来优化这个过程,这一节中主要介绍使用Apache
commons-daemon来生成系统服务,让我们的springboot项目在后台运行。Apache
commons-daemon的相关资料,大家可以直接去官方网站查找,这个应用很广,我们最熟悉的Tomcat就是使用Apache
commons-daemon中的Procrun来制作安装包的,这里我们将自己的程序使用它来制作系统服务。


一、Apache commons-daemon下载

到Apache
commons-daemon的官网下载,这里有个坑,官网下载的是commons-daemon-1.3.4的开发包,但是我们需要使用Windows系统下的本地二进制包,比如:commons-daemon-1.3.4-bin-windows.zip这样的,但在官网上没找到,这里附上下载地址:点击我跳转 这里我们选择最新的版本:commons-daemon-1.3.4-bin-windows.zip 下载后应该是这样的:

java swing 打包exe meven配置 java打包exe工具_java

这里需要特别注意,prunmgr.exe是32位系统的,如果你的系统是64位,需要amd64下面的prunmgr.exe。我这里使用的是64

二、项目改造并制作Windows服务

使用commons-daemon来制作服务的过程需要可执行的jar包,而不是EXE文件,而且springboot默认的启动类是org.springframework.boot.loader.JarLauncher,这个类修改了CLASSPATH的加载规则,会导致commons-daemon在加载时出现找不到类的情况,所以我们必须改造一下springboot项目的启动类,首先我们还是继续来改造上一章节我们的项目结构,改造后的结构如下:

java swing 打包exe meven配置 java打包exe工具_apache_02


然后修改pom.xml:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>org.example.testmvnpkgexespringboot.TestMvnPkgExeSpringbootApplication</mainClass>
        <excludes>
            <exclude>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </exclude>
        </excludes>
    </configuration>
</plugin>

因为要使用jar包,并将commons-daemon的文件打包进去,我们修改assembly的打包规则:

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
    <id>package</id>
    <formats>
        <format>zip</format>
    </formats>
    <includeBaseDirectory>true</includeBaseDirectory>
    <fileSets>
        <fileSet>
            <directory>runtime</directory>
            <outputDirectory>\runtime</outputDirectory>
        </fileSet>
        <fileSet>
            <directory>conf</directory>
            <outputDirectory>\</outputDirectory>
            <includes>
                <include>*.properties</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>bat</directory>
            <outputDirectory>\</outputDirectory>
            <includes>
                <include>*.bat</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>daemon</directory>
            <outputDirectory>\</outputDirectory>
            <includes>
                <include>*.exe</include>
            </includes>
        </fileSet>
        <fileSet>
            <directory>${project.build.directory}</directory>
            <outputDirectory>\</outputDirectory>
            <includes>
<!--                <include>pkg-sb.exe</include>-->
                <include>pkg-sb.jar</include>
            </includes>
        </fileSet>
    </fileSets>
</assembly>

把脚本和daemon的文件加入进来,然后运行打包命令:

mvn clean -DskipTests package

生成的zip解压后结构如下:

java swing 打包exe meven配置 java打包exe工具_windows_03

这里的2个bat脚本就是安装、卸载服务的脚本

安装脚本installService.bat如下:

@echo off
set SERVICE_EN_NAME=PkgSb
set SERVICE_CH_NAME=Package SB
set SERVICE_CH_DESC=Package SB
set BASEDIR=%CD%
set JAVA_HOME=%BASEDIR%\runtime
set DATA_DIR=%BASEDIR%\data
set CONFIG_FILE=%BASEDIR%\config.properties
set CLASSPATH=%BASEDIR%\pkg-sb.jar
set MAIN_CLASS=org.springframework.boot.loader.JarLauncher
set START_PARAM=
set STOP_CLASS=org.springframework.boot.loader.JarLauncher

set SRV=%BASEDIR%\prunsrv.exe

set LOGPATH=%BASEDIR%\logs

echo SERVICE_NAME: %SERVICE_EN_NAME%
echo JAVA_HOME: %JAVA_HOME%
echo MAIN_CLASS: %MAIN_CLASS%
echo prunsrv path: %SRV%

if "%JVM%" == "" goto findJvm
if exist "%JVM%" goto foundJvm
:findJvm
set "JVM=%JAVA_HOME%\bin\server\jvm.dll"
if exist "%JVM%" goto foundJvm
echo can not find jvm.dll automatically,
echo please use COMMAND to localation it
echo then install service
goto end
:foundJvm
echo install service...
"%SRV%" //IS//%SERVICE_EN_NAME% ^
        --DisplayName="%SERVICE_CH_NAME%" ^
        --Description="%SERVICE_CH_DESC%" ^
        --Install="%SRV%" ^
        --Classpath="%CLASSPATH%" ^
        --JavaHome="%JAVA_HOME%" ^
        --Jvm="%JVM%" ^
        --JvmMs=256 ^
        --JvmMx=512 ^
        --Startup=auto ^
        ++JvmOptions=-Dfile.encoding=utf-8 ^
        ++JvmOptions=-Dvar1=var1-value-001 ^
        ++JvmOptions=-Dconfig.file=%CONFIG_FILE% ^
        ++JvmOptions=-Ddata.dir=%DATA_DIR% ^
        --StartMode=jvm ^
        --StartClass=%MAIN_CLASS% ^
        --StartMethod=main ^
        ++StartParams="%START_PARAM%" ^
        --StopMode=jvm ^
        --StopClass=%STOP_CLASS% ^
        --StopMethod=main ^
        --StopParams=stop^
        --LogPath=%LOGPATH% ^
        --StdOutput=auto ^
        --StdError=auto ^
        --PidFile=%LOGPATH%\pid
echo install service successfully
pause

这里我们指定的服务名称以及JDK的位置以及其他的运行参数。

卸载脚本uninstallService.bat如下:

@echo off
set BASEDIR=%CD%
echo %BASEDIR%
set SERVICE_NAME=PkgSb
set SRV=%BASEDIR%\prunsrv.exe
echo uninstall service...
"%SRV%" //DS//%SERVICE_NAME%
echo uninstall service successfully
pause

三、运行并测试服务

完成上面的过程后,我们将打包好的zip拷贝到Windows下解压,然后双击installService.bat运行:

java swing 打包exe meven配置 java打包exe工具_maven_04

这样表示我们的服务安装成功,现在我们到系统服务里去查看一下:

java swing 打包exe meven配置 java打包exe工具_spring boot_05

说明已经安装成功,现在我们来运行,右键点击启动服务:

java swing 打包exe meven配置 java打包exe工具_maven_06


我们可以看到服务已经启动成功,而且是开机自启动

下面我们再来测试服务是否正常,在浏览器中访问:

java swing 打包exe meven配置 java打包exe工具_windows_07


可以看到和我们上一节测试的结果一样,传递的系统变量的值都能拿到。

最后我们再来测试一下,卸载服务,点击uninstallService.bat:

java swing 打包exe meven配置 java打包exe工具_java_08


然后到系统服务中查看,已经卸载成功了。


总结

1、使用Apache
commons-daemon来制作Windows服务过程相对比较麻烦,而且commons-daemon对服务的停止有很严格的要求,只要是StopClass和StopMethod两个参数,当我们卸载服务时,如果这里配置的方法无法正常停止,那么这个操作将会失败,只有去杀死进程后再来卸载。

2、springboot打包后启动类使用的是org.springframework.boot.loader.JarLauncher,这这个类改写了classpath的加载规则,因此如果在commons-daemon的StopClass里指定我们自己写的类,将会报错,这里我们用了一个技巧,就是停止和启动都使用一个方法,然后通过参数来决定是启动项目还是停止项目,这也能和commons-daemon的配置参数能吻合,从而解决卸载服务时不成功的问题。具体见代码:

public static void main(String[] args) {
    if(args!=null && args.length>0 && args[0].equals("stop")){
        System.exit(0);
        return;
    }
    SpringApplication.run(TestMvnPkgExeSpringbootApplication.class, args);
}

3、commons-daemon的运行日志在目录下有个logs文件夹中,可以在这里查看日志信息

4、最常见的错误信息如下: [error] [13308] %1 不是有效的 Win32 应用程序
这个错误的原因是因为我们的prunsrv.exe文件使用32位系统,需要用amd64下面的64位系统的文件。

5、commons-daemon是个可选方案,配置比较复杂,且需要处理的细节很多,但出自Apache大厂,至少可靠性有保障。下一节我会给大家分享一个更丝滑的Windows系统服务制作工具