一、将exe程序变为Win服务
即将java启动批处理命令编译为win程序,可通过
批处理潜行者V6.0
或
quickbfc
3.6.1
实现;
然后利用系统自带的sc命令将其创建为系统服务:(这里为了测试效果,我加上了交互式运行服务的参数)
如果要使用type参数,则一定要设置两个,比如上面的栗子中加了第一个”独立运行“,后面还要跟一个”交互式“才能最终生效,
否则会报错:
[SC]
CreateService FAILED 87
参考:
How to create windows services (Command Line)
Using some of the options will trigger a "[SC]
CreateService FAILED 87" this usually means the option used like "type" needs
another declaration. In case for instance when using type=
interact, the type= option must be declared again
with an alternative type like own. So effectivly the service type will be
type= own interactive.
创建完成之后就可以启动服务:
net
start test
发现程序正常启动了,但是服务一直是启动状态,把程序关闭后,服务又变成了停止状态。Why?
因为系统服务并不知道你程序启动到什么状态才算是启动成功,也不知道程序停止是正常结束还是异常退出。
归根结底,就是通过sc命令将一般程序添加为系统服务的方式没有实现Win服务接口,不满足作为一个标准的daemon程序的条件。
在网上搜索一番,发现还有微软的开发工具可以利用:
使用srvany.exe将任何程序作为Windows服务运行
上面这篇文章写得很棒,作者还开发了一个轻巧的工具
SrvanyUI
方便创建自定义服务。
但是我经过试验没有成功,原因不明。后台监控发现java程序闪现之后迅速关闭,可能是因为classpath设置错误。
总而言之,这种方式还是不够灵活,出错没有任何提示,只能通过经验去排错;
另外,如文中所说,该工具已不被微软支持了,并且win7以上的版本可能会出现兼容问题。
二、来自Java世界的工具
How to create a windows service from java
各种牛人都在这篇问答里发表了他们的看法:
The Java Service Wrapper
,目前业界最知名、最成熟的解决方案
JSW我使用了较长的时间,稳定性和扩展性都不错,高级版本还提供了对Java异常的处理,比如OOM产生时可以选择是否自动重启服务。
但不足之处是,收费、64位版本需要购买Licence。放在5年前,或许不用担忧是否需要64位JVM,但如今,32位仅1.5G的堆内存略显不足。
一个
wrapper.conf
配置文件示例如下:(官方下载包有更详细的例子和说明)
wrapper.java.
command
=
"C:\Program
Files\Java\jdk1.7.0_72\bin\java"
wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
wrapper.java.classpath.1=..
/test
.jar
wrapper.java.classpath.2=wrapper.jar
wrapper.java.classpath.3=..
/lib/
*
wrapper.java.library.path.1=.
wrapper.java.additional.1= -Xms512m
wrapper.java.additional.2= -Xmx512m
wrapper.java.initmemory=256
wrapper.java.maxmemory=512
wrapper.app.parameter.1=
test
.Main
wrapper.app.parameter.2=arg1
wrapper.console.
format
=PM
wrapper.console.loglevel=INFO
wrapper.logfile=..
/log/test
.log
wrapper.logfile.
format
=LPTM
wrapper.logfile.loglevel=INFO
wrapper.logfile.maxsize=0
wrapper.logfile.maxfiles=0
wrapper.syslog.loglevel=NONE
wrapper.console.title=Test_Service
wrapper.ntservice.name=Test_Service
wrapper.ntservice.displayname=Test_Service
wrapper.ntservice.description=Test_Service
wrapper.ntservice.dependency.1=
wrapper.ntservice.starttype=AUTO_START
wrapper.ntservice.interactive=
false
Yet Another Java Service Wrapper
,类似JSW的开源实现版本
或许YAJSW出现就是为了替换JSW,因为它是开源免费的,且支持
从JSW快速无缝迁移
。但是,有个
哥们都说被它19M的下载包给吓到了…所以……
Apache Commons Daemon
,著名的Apache Commons工具包的成员
WinRun4J
,java2exe2service集合,额外提供 eclipse插件一键导出!
以下工具本人并未测试,但不代表不适用:
JSmooth
(07年后未更新)
NSSM -
the Non-Sucking Service Manager
(名字很叼的样子,提供GUI方式将EXE打包为服务,与上文的SrvanyUI类似)
WINDOWS
SERVICE WRAPPER
(6年未更新且依赖.Net)
FireDaemon
(看到Buy Now两个字后我就X了)
javaservice
(06年后未更新,使用方法不详)
Java Service
Launcher
(小巧,支持64位JVM以及JRockit等第三方虚拟机,可惜文档比较简陋)
Launch4j
,一个将Jar打包为exe的工具,可惜的是使用XML配置,个人无感
更多开源工具见:
Java开源打包工具
三、久违的 Apache Commons Daemon
先来说说情怀:
Tomcat,相信很多同学从第一天学习Java的时候就开始熟知。
看到ACD的图标后才发现,原来Tomcat win安装版就是用的它!
再来说说用法:Win版的入口程序是
procrun
,用它来创建一个服务是如此之简单:
set
CP=%APP_HOME%\
test
.jar;%APP_HOME%\lib\*
REM 创建服务
prunsrv
//IS//test_service
--DisplayName=test_service
--Install=%APP_HOME%\bin\prunsrv.exe --StartMode=jvm --StopMode=jvm
--Startup=auto --JavaHome=
"C:\Program Files\Java\jdk1.7.0_72\" --JvmMs=2048
--JvmMx=2048 --JvmSs=128
++JvmOptions=-server;-XX:+UseParallelGC;-XX:ParallelGCThreads=4;-XX:+UseParallelOldGC;-XX:+UseAdaptiveSizePolicy
--Classpath=%CP%"
--StartClass=com.
test
.Main --StopClass=com.
test
.Main --StopMethod=stop --LogPath=%APP_HOME%\log
--StdOutput=auto --StdError=auto
REM 删除服务
prunsrv
//DS//test_service
参数很多,看看注释就明白了,这里也有中文翻译:
Commons Daemon 之 procrun
。
此外,
Wiki页
上有一些答问和一个Java主程序栗子。有几点需要注意的是:
--LogPath
一定要指定,下面的报错信息都可在日志文件中找到:
commons-daemon.2014-11-11.log
--JavaHome
一定要写JRE路径,否则可能报错:
Prunsrv.C Failed Creating Java
--Classpath
一定要写成变量,否则会添加失败,原因不详
--Classpath=%CP%"
这句最后多出的那个双引号一定要写,否则创建失败,原因不详!
--StopMethod=stop
关闭服务的时候会自动调用该stop方法,具体写法如下:
public
static
void
stop(String[] args) {
System.out.println(
"服务关闭了"
);
System.exit(
0
);
Procrun不仅可将Java类生成服务,也可将exe变为服务,详见参数设置。
参考:
用common-deamon构建java后台服务
、
Windows 64位环境的Java 服务配置
四、最后来看强大的WinRun4J
先来说说它的弊端:需要一个入口类,依赖WinRun4J.jar包,相比ACD有一定耦合度,而非直接用主启动类。除此之外,一切看起来都不错~
The only drawback is that it requires a special class for
working as a service (instead of simply calling standard main
class)
官网上有个生成服务的栗子,下载的包里也有,但是都有点问题:
import
org.boris.winrun4j.AbstractService;
import
org.boris.winrun4j.EventLog;
import
org.boris.winrun4j.ServiceException;
public
class
ServiceTest
extends
AbstractService {
public
int
serviceMain(String[] args)
throws
ServiceException {
int
count =
0
;
while
(!shutdown) {
try
{
Thread.sleep(
1000
);
}
catch
(InterruptedException e) {
if
(++count %
10
==
0
) {
EventLog.report(
"test"
, EventLog.INFORMATION,
"test..."
+ count);
System.out.println(
"test log...."
+ count);
System.out.println(
"service shutdown..."
);
return
0
;
public
boolean
isShutdown() {
System.out.println(
"isShutdown called..."
);
return
super
.isShutdown();
上面类中调用
EventLog.report
方法生成的日志只会写到系统日志里,只有在
计算机管理 - 系统工具 - 事件查看器 - 应用程序
中才能看到
对应的日志文件在XP系统中的位置是:
C:\WINDOWS\system32\config\
SysEvent.Evt
显然我们更希望日志可控,而不是被操作系统接管。但是我没有找到合适的参数来设置,只能通过Java程序来指定。
将上面的类编译后生成class文件,然后再来编辑配置文件
WinRun4Jc.ini
:
service.description=
test
WinRun4J.
classpath.1=.
classpath.2=D:\winrun4j\bin\WinRun4J.jar
log=
test
.log
WinRun4Jc.ini
与
WinRun4Jc.exe
必须位于相同目录且名称要一致,因为没有参数可以指定配置文件路径…
以上参数缺一不可,并且
service.id
与
service.name
一定要写相同的服务名称
classpath.2
指定为
WinRun4J.jar
的路径,因为上面的服务入口类实现了该包中的某类
通过以下命令创建和删除服务: