Java程序注册Windows服务、Springboot项目注册windows服务的几种方式、Springboot优雅关闭
Java程序注册Windows服务、Springboot项目注册windows服务的几种方式
3种方法:
1、利用JAVA Service Wrapper
缺点:官方分专业版、标准版和社区版,64位没有社区版,非社区版收费;
2、使用WinSW,
地址:http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/
缺点:需要.Net运行环境支持
优点:网上资料很多,简单,稳定可靠;
3、使用Apache的Procrun
地址:http://commons.apache.org/proper/commons-daemon/procrun.html
优点:tomcat使用的就是这个,稳定可靠,网上资料也不少,不依赖任何环境,有任务栏通知图标
本文重点介绍第3中方案
Apache的Procrun
依据JDK版本使用不同的服务程序,根目录的服务程序时32位的
管理UI程序需要改名,名字和windows服务相同,必须是英文ID
目录结构图
我的服务ID是vpm-gather_x64,上面的都比较简单,主要是脚本怎么写,直接上脚本:
安装脚本:运行时必须使用管理员权限
@echo off
cd /d "%~dp0"
rem 设置服务ID,使用当前目录名
for /f "delims=" %%i in ("%cd%") do set SERVICE_ID=%%~ni
echo SERVICE_ID: %SERVICE_ID%
rem 设置程序名称
set SERVICE_NAME=管理服务(服务中文名)
set SERVICE_DESC=服务中文描述。
rem 设置程序依赖及程序入口类
set BASE_HOME=%CD%
echo BASE_HOME: %BASE_HOME%
rem 自动识别jar包,本目录只能存在一个jar包
for /f "delims=" %%i in ('dir /b *.jar') do set JAR_FILE=%%i
set CLASSPATH=%BASE_HOME%\%JAR_FILE%
echo JAR_FILE: %CLASSPATH%
set MAIN_CLASS=org.springframework.boot.loader.JarLauncher
set STOP_CLASS=SpringbootProcrunStopClass
rem 设置prunsrv路径
set SRV=%BASE_HOME%\prunsrv.exe
echo SRV: %SRV%
rem 设置日志路径及日志文件前缀
set LOGPATH=%BASE_HOME%\logs
rem 设置jvm
if "%JVM%" == "" goto findJvm
if exist "%JVM%" goto foundJvm
:findJvm
set "JVM=%BASE_HOME%\jre\bin\server\jvm.dll"
echo JVM: %JVM%
if exist "%JVM%" goto foundJvm
echo 没有找到JVM,请确认安装包是否完整。
goto end
:foundJvm
echo 安装服务...
"%SRV%" //IS//%SERVICE_ID% --DisplayName="%SERVICE_NAME%" --Description="%SERVICE_DESC%" "--Classpath=%CLASSPATH%" "--Install=%SRV%" "--JavaHome=%BASE_HOME%" "--Jvm=%JVM%" --JvmMs=256 --JvmMx=1024 --Startup=auto --JvmOptions=-Djcifs.smb.client.dfs.disabled=false ++JvmOptions=-Djcifs.resolveOrder=DNS;-Dlogging.path="%BASE_HOME%\logs" --StartMode=jvm --StartClass=%MAIN_CLASS% --StartMethod=main --StopMode=jvm --StopClass=%STOP_CLASS% --StopMethod=main --StopTimeout=30 --StopParams= --LogPath=%LOGPATH% --StdOutput=auto --StdError=auto
echo 安装完成,创建服务管理器 SERVICE_ID: %SERVICE_ID% ...
set MGR=%BASE_HOME%\%SERVICE_ID%.exe
rem 创建任务栏图标启动文件,适用Windows10,其它操作系统按实际情况修改
echo %MGR% //MS//%SERVICE_ID% > "%SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\%SERVICE_ID%.bat"
rem 显示任务栏图标并启动服务
echo 任务栏出现图标后,请主动关闭此窗口!!!
"%MGR%" //MR//%SERVICE_ID%
:end
pause
卸载脚本:运行时必须使用管理员权限
@echo off
cd /d "%~dp0"
for /f "delims=" %%i in ("%cd%") do set SERVICE_ID=%%~ni
set basedir=%CD%
echo 卸载服务管理器 SERVICE_ID: %SERVICE_ID% ...
"%BASEDIR%\%SERVICE_ID%.exe" //MQ//%SERVICE_ID%
del "%SYSTEMDRIVE%\ProgramData\Microsoft\Windows\Start Menu\Programs\StartUp\%SERVICE_ID%.bat"
echo 正在卸载服务 SERVICE_ID: %SERVICE_ID% ...
"%BASEDIR%\prunsrv.exe" //DS//%SERVICE_ID%
echo 服务卸载完毕。
pause
解决Springboot项目服务关闭失败问题:
Procrun关闭服务需要调用一个类的一个方法,项目结构同mian函数,类在项目中调用不了,必须在springboot loader中,否则服务关闭不了,本人没有找到springboot项目的boot loader关闭方式,方法如下:
1、编写springboot loader调用类:
import java.lang.reflect.Method;
public class SpringbootProcrunStopClass {
private static Object CALLBACK;
private static final String METHOD = "stop";
public static final void regedit(Object callback) {
CALLBACK = callback;
}
public static final void main(String[] args) {
try {
if (CALLBACK != null) {
Class<?> clazz = CALLBACK.getClass();
Method method = clazz.getMethod(METHOD, String[].class);
Object _args = args;
method.invoke(CALLBACK, _args);
} else {
System.err.println("停止服务调用失败,未注册停止回调对象。");
}
}catch(java.lang.Throwable t) {
t.printStackTrace();
}
}
}
编译后放到springboot项目jar包跟目录,便于根classloader加载:
2、springboot项目启动类(项目中其它的类也可以,但是必须在启动完成后调用注册)
package com.demo.vpm.gather;
import java.lang.reflect.Method;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication(scanBasePackages = {"com.demo.vpm.gather"})
public class VpmGatherApplication extends SpringBootServletInitializer {
private transient static final Logger LOGGER = LogManager.getLogger();
private static ApplicationContext CONTEXT = null;
public static void main(String[] args) {
LOGGER.info("系统开始启动......");
ConfigurableApplicationContext ctx = SpringApplication.run(VpmGatherApplication.class, args);
CONTEXT = ctx;
try {
Class<?> c = Class.forName("SpringbootProcrunStopClass");
Method m = c.getMethod("regedit", Object.class);
m.invoke(c, new StopCallback());
LOGGER.info("系统关闭服务注册成功。");
} catch (Exception e) {
LOGGER.warn("未找到关闭服务类,回调注册失败:{}", e.toString());
}
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(VpmGatherApplication.class);
}
public static void stop(String[] args) {
if (CONTEXT != null) {
LOGGER.info("系统开始关闭......");
int exitCode = SpringApplication.exit(CONTEXT, (ExitCodeGenerator) () -> 0);
LOGGER.info("系统开始关闭结果:{}", exitCode);
System.exit(exitCode);
} else {
LOGGER.error("系统运行环境不存在,无法执行关闭操作。");
}
}
public static class StopCallback{
public void stop(String[] args) {
VpmGatherApplication.stop(args);
}
}
}
做完就可以试试了
本文地址:https://blog.csdn.net/kxlele/article/details/110136308