Spring启动JAR作为Windows服务

时间:2014-06-09 15:58:27

标签: spring-boot procrun

我试图用procrun包装弹簧靴“uber JAR”。

按预期运行以下工作:

  

java -jar my.jar

我需要弹簧启动jar才能在Windows启动时自动启动。最好的解决方案是将jar作为服务运行(与独立的tomcat相同)。

当我尝试运行此操作时,我收到“Commons Daemon procrun失败并退出值:3”

查看spring-boot源,它看起来好像使用自定义类加载器:

  

https://github.com/spring-projects/spring-boot/blob/master/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java

尝试直接运行main方法时,我也得到一个“ClassNotFoundException”。

  

java -cp my.jar my.MainClass

有没有一种方法可以用来在spring boot jar中运行我的main方法(不是通过JarLauncher)?

有没有人成功将spring-boot与procrun集成?

我知道http://wrapper.tanukisoftware.com/。但是由于他们的许可我不能使用它。

更新

我现在设法使用procrun启动服务。

set SERVICE_NAME=MyService
set BASE_DIR=C:\MyService\Path
set PR_INSTALL=%BASE_DIR%prunsrv.exe

REM Service log configuration
set PR_LOGPREFIX=%SERVICE_NAME%
set PR_LOGPATH=%BASE_DIR%
set PR_STDOUTPUT=%BASE_DIR%stdout.txt
set PR_STDERROR=%BASE_DIR%stderr.txt
set PR_LOGLEVEL=Error

REM Path to java installation
set PR_JVM=auto
set PR_CLASSPATH=%BASE_DIR%%SERVICE_NAME%.jar

REM Startup configuration
set PR_STARTUP=auto
set PR_STARTIMAGE=c:\Program Files\Java\jre7\bin\java.exe 
set PR_STARTMODE=exe
set PR_STARTPARAMS=-jar#%PR_CLASSPATH%

REM Shutdown configuration
set PR_STOPMODE=java
set PR_STOPCLASS=TODO
set PR_STOPMETHOD=stop

REM JVM configuration
set PR_JVMMS=64
set PR_JVMMX=256

REM Install service
%PR_INSTALL% //IS//%SERVICE_NAME%

我现在只需要锻炼如何停止服务。我正在考虑使用弹簧启动执行器关闭JMX Bean。

此刻停止服务时会发生什么? Windows无法停止服务(但标记为已停止),服务仍在运行(我可以浏览到localhost),任务管理器中没有提到该进程(不是很好!除非我是盲人)。< / p>

5 个答案:

答案 0 :(得分:5)

我遇到了类似的问题,但发现其他人(Francesco Zanutto)非常慷慨地写了一篇关于他们努力的博客文章。 他们的解决方案对我有用。我们没有相信他们实现这段代码的时间。

http://zazos79.blogspot.com/2015/02/spring-boot-12-run-as-windows-service.html

与我在示例中看到的exe模式相比,他正在使用jvm启动和停止模式。有了这个,他就可以扩展Spring Boot的JarLauncher来处理来自Windows服务的“开始”和“停止”命令,我相信你们希望这些命令能够正常关闭。

与他的示例一样,您将添加多个主要方法,具体取决于您的实现,您需要指明启动器现在应该调用哪些方法。我正在使用Gradle,只需将以下内容添加到我的build.gradle:

springBoot{
    mainClass = 'mydomain.app.MyApplication'
}

我的Procrun安装脚本:

D:\app\prunsrv.exe //IS//MyServiceName ^
--DisplayName="MyServiceDisplayName" ^
--Description="A Java app" ^
--Startup=auto ^
--Install=%CD%\prunsrv.exe ^
--Jvm=%JAVA_HOME%\jre\bin\server\jvm.dll ^
--Classpath=%CD%\SpringBootApp-1.1.0-SNAPSHOT.jar; ^
--StartMode=jvm ^
--StartClass=mydomain.app.Bootstrap ^
--StartMethod=start ^
--StartParams=start ^
--StopMode=jvm ^
--StopClass=mydomain.app.Bootstrap ^
--StopMethod=stop ^
--StopParams=stop ^
--StdOutput=auto ^
--StdError=auto ^
--LogPath=%CD% ^
--LogLevel=Debug

JarLauncher扩展类:

package mydomain.app;


import org.springframework.boot.loader.JarLauncher;
import org.springframework.boot.loader.jar.JarFile;

public class Bootstrap extends JarLauncher {

    private static ClassLoader classLoader = null;
    private static Bootstrap bootstrap = null;

    protected void launch(String[] args, String mainClass, ClassLoader classLoader, boolean wait)
            throws Exception {
        Runnable runner = createMainMethodRunner(mainClass, args, classLoader);
        Thread runnerThread = new Thread(runner);
        runnerThread.setContextClassLoader(classLoader);
        runnerThread.setName(Thread.currentThread().getName());
        runnerThread.start();
        if (wait == true) {
            runnerThread.join();
        }
    }

    public static void start (String []args) {
        bootstrap = new Bootstrap ();
        try {
            JarFile.registerUrlProtocolHandler();
            classLoader = bootstrap.createClassLoader(bootstrap.getClassPathArchives());
            bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
        }
        catch (Exception ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }

    public static void stop (String []args) {
        try {
            if (bootstrap != null) {
                bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
                bootstrap = null;
                classLoader = null;
            }
        }
        catch (Exception ex) {
            ex.printStackTrace();
            System.exit(1);
        }
    }

    public static void main(String[] args) {
        String mode = args != null && args.length > 0 ? args[0] : null;
        if ("start".equals(mode)) {
            Bootstrap.start(args);
        }
        else if ("stop".equals(mode)) {
            Bootstrap.stop(args);
        }
    }

}

我的主要Spring应用程序类:

package mydomain.app;

import java.lang.management.ManagementFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan
@EnableAutoConfiguration
public class MyApplication {

    private static final Logger logger = LoggerFactory.getLogger(MyApplication.class);
    private static ApplicationContext applicationContext = null;

    public static void main(String[] args) {
        String mode = args != null && args.length > 0 ? args[0] : null;

        if (logger.isDebugEnabled()) {
            logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application mode:" + mode + " context:" + applicationContext);
        }
        if (applicationContext != null && mode != null && "stop".equals(mode)) {
            System.exit(SpringApplication.exit(applicationContext, new ExitCodeGenerator() {
                @Override
                public int getExitCode() {
                    return 0;
                }
            }));
        }
        else {
            SpringApplication app = new SpringApplication(MyApplication.class);
            applicationContext = app.run(args);
            if (logger.isDebugEnabled()) {
                logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application started context:" + applicationContext);
            }
        }
    }
}

答案 1 :(得分:5)

现在可以使用winsw从Spring Boot 1.3开始。

documentation会将您定向到显示如何设置服务的reference implementation

答案 2 :(得分:4)

从springboot v1.2.2开始,没有干净的方法可以使用procrun关闭一个打包为超级jar的Spring Boot应用程序。请务必遵循这些问题,因为这也是其他人要问的问题:

目前还不清楚springboot维护者是否会如何处理它。在此期间,考虑解压缩超级jar并忽略Spring Boot的JarLauncher。

我对这个问题的原始答案(在历史中可见)提出了一种应该起作用的方式(我认为这样做了),但不是由于在JarLauncher中如何处理类加载器。

答案 3 :(得分:2)

刚刚遇到这个并希望分享,我一段时间后修复了这个问题并发出了拉取请求。 https://github.com/spring-projects/spring-boot/pull/2520

您可以使用我的分叉版本,直到它合并为使用procrun开始/停止。

答案 4 :(得分:-1)

远离winsw,它是用.NET制作的,我在客户环境中遇到了很多关于过时Windows的问题。

我推荐NSSM,它是使用纯C制作的,我在所有过时的Windows上使用它没有问题。它具有相同的功能和更多...

以下是batch script (.bat)示例如何使用它:

rem Register the service
nssm install my-java-service "C:\Program Files\Java\jre1.8.0_152\bin\java.exe" "-jar" "snapshot.jar"
rem Set the service working dir
nssm set my-java-service AppDirectory "c:\path\to\jar-diretory"
rem Redirect sysout to file
nssm set my-java-service AppStdout "c:\path\to\jar-diretory\my-java-service.out"
rem Redirect syserr to file
nssm set my-java-service AppStderr "c:\path\to\jar-diretory\my-java-service.err"
rem Enable redirection files rotation
nssm set my-java-service AppRotateFiles 1
rem Rotate files while service is running
nssm set my-java-service AppRotateOnline 1
rem Rotate files when they reach 10MB
nssm set my-java-service AppRotateBytes 10485760
rem Stop service when my-java-service exits/stop
nssm set my-java-service AppExit Default Exit
rem Restart service when my-java-service exits with code 2 (self-update)
nssm set my-java-service AppExit 2 Restart
rem Set the display name for the service
nssm set my-java-service DisplayName "My JAVA Service"
rem Set the description for the service
nssm set my-java-service Description "Your Corp"
rem Remove old rotated files (older than 30 days)
nssm set my-java-service AppEvents Rotate/Pre "cmd /c forfiles /p \"c:\path\to\jar-diretory\" /s /m \"my-java-service-*.*\" /d -30 /c \"cmd /c del /q /f @path\""
rem Make a copy of my-java-service.jar to snapshot.jar to leave the original JAR unlocked (for self-update purposes)
nssm set my-java-service AppEvents Start/Pre "cmd /c copy /y \"c:\path\to\jar-diretory\my-java-service.jar\" \"c:\path\to\jar-diretory\snapshot.jar\""