Windows系統服務(NT服務)相對於普通應用程序最直接的一個優點是系統啟動后就可直接運行而無需用戶登錄系統。事實上,作為服務器來使用的系統,通常也並不需要登錄系統,這樣不只是方便,也提升了系統的安全性。不過,通常情況下,Windows系統服務使用C或C++實現,而有些時候基於某些因素的考慮,我們期望使用Java來實現系統服務,可以借助開源的JavaService達到此目的。
以下演示其實現過程。
首先編寫實現NT服務的Java類,以下的示例代碼通過兩個類來達到實現NT服務的目的。類TestService提供了NT服務啟動及停止的控制方法,而類Service則實現了NT服務真正要完成的工作。類TestService及類Service的完整代碼如下:
package com.shuilangyizu; import java.util.Calendar; public class TestService { private static Thread thread=null; private static Service service = null; /** * 退出服務方法(該方法必須有參數 String [] args) * @param args */ public static void StopService(String[] args) { System.out.println("停止服務"); service.setRunFlag(false); } /** * 啟動服務方法(該方法必須有參數 String [] args) * @param args */ public static void StartService(String[] args) { System.out.println("啟動服務"); // 產生服務線程 service = new Service(); thread=new Thread(service); try { // 將服務線程設定為用戶線程,以避免StartService方法結束后線程退出 thread.setDaemon(false); if(!thread.isDaemon()) { System.out.println("成功設定線程為用戶線程!"); } //啟動服務線程 thread.start(); } catch(SecurityException se) { System.out.println("線程類型設定失敗!"); } } } class Service implements Runnable { private boolean runFlag = true; /** * 設定服務線程運行標志值 * @param runFlag */ public synchronized void setRunFlag(boolean runFlag) { this.runFlag = runFlag; } /** * 取得服務線程運行標志值 * @param void */ private synchronized boolean getRunFlag() { return runFlag; } @Override public void run() { System.out.println("服務線程開始運行"); while(getRunFlag()) { Calendar cal = Calendar.getInstance(); long mis = cal.getTimeInMillis(); System.out.println("當前時間:" + mis); try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("服務線程結束運行"); } }
類TestService中的方法StartService用於啟動NT服務,而StopService方法則用於停止NT服務。從代碼中可以看出方法StartService與StopService實際上是分別實現了啟動線程與終止線程的功能,而這里的線程就是實現NT服務的線程。這里有兩點要特別注意:
1. 方法StartService與StopService都擁有參數String[] args,在安裝NT服務時,可以指定啟動或停止服務的參數,這些指定的參數將可以通過String[] args傳遞給方法StartService與StopService。在本例中由於演示的功能較為簡單,故而未曾使到該參數。
2. 在StartService方法中啟動線程之前,有必要將線程設定為用戶線程。其原因在於如果線程是一個后台線程,則當主程序結束后,JVM會自動退出,后台線程當然也就終止執行,而如果主程序結束時,還有用戶線程在運行,則JVM不會自動退出,而是要等用戶線程結束后才退出。因此,要保證NT服務正常運行,這一點也是要特別注意的。
類Service是實現NT服務的線程類,NT服務要完成的功能在方法run中完成,在示例中它僅僅輸出了當前時間,在實際應用中應根據需要在其中完成更為復雜的功能。方法setRunFlag與getRunFlag分別用於設定線程是否運行的標志和取得該標志值。setRunFlag方法需要在類外被調用並通過設定不同的參數通知線程是否需要繼續執行;getRunFlag方法則取得該標志,它只在方法run的循環中被調用,所以聲明為私有方法。此外,由於在不同線程中對線程運行標志進行設定與讀取,所以方法setRunFlag與getRunFlag被設定為同步方法。
代碼編寫完成后,執行編譯過程,必要時還可以打包成jar文件。
隨后的工作就是利用JavaService注冊NT服務,JavaService是一個開源項目,其項目地址為http://javaservice.objectweb.org,打開該地址后下載JavaService壓縮包,並將解壓之后的JavaService.exe置於上述代碼編譯之后包所在的目錄,或者將JavaService.exe所在目錄添加到環境變量PATH之中。
JavaService一共提供了8個參數可供選擇,其中我們只需要關心安裝NT服務的-install參數和卸載NT服務的-uninstall參數。
使用-install參數安裝NT服務時還需要提供與服務相關的其它一些參數,其命令格式如下:
JavaService -install service_name jvm_library [jvm_options]
-start start_class [-method start_method] [-params (start_parameters)]
[-stop start_class [-method stop_method] [-params (stop_parameters)]]
[-out out_log_file] [-err err_log_file]
[-current current_dir]
[-path extra_path]
[-depends other_service]
[-auto | -manual]
[-shutdown seconds]
[-user user_name -password password]
[-append | -overwrite]
[-startup seconds]
[-description service_desc]
相關參數的作用說明如下:
service_name: The name of the service.
jvm_library: The location of the JVM DLL used to run the service.
jvm_option: An option to use when starting the JVM, such as:
"-Djava.class.path=c:/classes" or "-Xmx128m".
start_class: The class to load when starting the service.
start_method: The method to call in the start_class. default: main
start_parameters:Parameter(s) to pass in to the start_method.
stop_class: The class to load when stopping the service.
stop_method: The method to call in the stop_class. default: main
stop_parameters: Parameter(s) to pass in to the stop_method.
out_log_file: A file to redirect System.out into. (gets overwritten)
err_log_file: A file to redirect System.err into. (gets overwritten)
current_dir: The current working directory for the service.
Relative paths will be relative to this directory.
extra_path: Path additions, for native DLLs etc. (no spaces)
other_service: Single service name dependency, must start first.
auto / manual: Startup automatic (default) or manual mode.
seconds: Java method processing time (startup:sleep, shutdown:timeout).
user_name: User specified to execute the service (user@domain).
password: Password applicable if user specified to run the service.
append / overwrite: Log file output mode, append (default) or overwrite.
service_desc: Text describing installed service (quoted string, max 1024).
要安裝前面我們用Java編寫的NT服務,可以用以下命令完成:
JavaService.exe -install TJS "%JAVA_HOME%/jre/bin/server/jvm.dll" -Xmx128m -Djava.class.path=%CLASSPATH% -start com.yanzhijun.TestService -method StartService -stop com.yanzhijun.TestService -method StopService -out "%CD%/out.log" -err "%CD%/err.log" -current "%CD%" –auto
上述命令中用“%”包含的是環境變量,其中JAVA_HOME是JDK的安裝目錄,CLASSPATH是為類的查找路徑,這兩者與正常配置JDK保持一致即可。CD則是當前目錄。
成功安裝服務以后,可以用以下命令啟動服務:
net start TJS
由於在安裝服務時指定了auto參數,所以服務被設定為自動啟動,因此機器重啟后無需使用上述命令這個NT服務就會啟動。
如果需要停止服務,可以使用下列命令:
net stop TJS
這樣就可以正常地使用Java所編寫的NT服務了,如果不需要該服務,則應將該服務卸載,卸載服務的命令相比安裝要簡單地多,其格式如下:
JavaService -uninstall service_name
例如要卸載剛才安裝的名稱為TJS的服務,可以執行以下命令:
JavaService -uninstall TJS
注意:Java代碼所生成的NT服務類所存儲的目錄路徑中不能包含中文,否則啟動服務時會失敗。