欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java程序注册Windows服务、Springboot项目注册windows服务的几种方式、Springboot优雅关闭

程序员文章站 2022-04-12 19:36:13
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....

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

Java程序注册Windows服务、Springboot项目注册windows服务的几种方式、Springboot优雅关闭依据JDK版本使用不同的服务程序,根目录的服务程序时32位的
管理UI程序需要改名,名字和windows服务相同,必须是英文ID

Java程序注册Windows服务、Springboot项目注册windows服务的几种方式、Springboot优雅关闭目录结构图
我的服务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加载:
Java程序注册Windows服务、Springboot项目注册windows服务的几种方式、Springboot优雅关闭
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);
		}
	}
}

做完就可以试试了
Java程序注册Windows服务、Springboot项目注册windows服务的几种方式、Springboot优雅关闭

Java程序注册Windows服务、Springboot项目注册windows服务的几种方式、Springboot优雅关闭

本文地址:https://blog.csdn.net/kxlele/article/details/110136308