优雅地关闭java console界面

对于发布后的jar或class,我们经常会写一些bat脚本来方便地使之运行,比如:
java -jar XXX.jar
一旦双击这个bat脚本,会启动一个黑色的console界面,如果不需要这个,可以修改为:
@echo off
start javaw -jar xxx.jar

某些时候,我们是需要这个console界面的,因为日志(system.out.print或logger.debug等)都可以很好地实时显示。但问题是:当需要关闭程序时,你指望别人会去按ctrl+C来终止,从而调用你的shutdownHook?呵呵,一般不会。那么,怎么实时看到日志,又可以优雅地关闭程序?我们有两个思路:

[list]
[*]双击bat后,执行自己的Java桌面应用,不显示console界面,把日志包括系统的system.out.print和log4j的日志都显示在自己的界面里。此时要关闭事件可以很容易捕捉。
[*]双击bat后,显示console界面,当用户关闭窗口时,捕获事件,回调java的善后处理方法。
[/list]
第一个方法经过测试是可以实现的,但貌似很暴力,而且大多时候我只是想运行的时候看看日志,为什么还要拿个swing程序来封装?第二个办法还是更人性化一些。下面我们就看看如何实现。

[b]实现依据[/b]:当用户关闭console窗口时,触发的是ctrl+close事件,[url=http://msdn.microsoft.com/en-us/library/windows/desktop/ms682535%28v=vs.85%29.aspx]详见这里[/url]。所以需要依靠JNI或是JNA来捕获底层事件。关于JNI的实现可以[url=http://www.rqna.net/qna/mvnxwx-windows-shutdown-hook-on-java-application-run-from-a-bat-script.html]参考这里[/url],下面我再整理一下:

[size=large][b]JNI实现:[/b][/size]

import java.io.File;
import java.io.IOException;

public class TestConsoleHandler {

private static Thread hook;

public static void main(String[] args) {
System.out.println("Start");
hook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(hook);
replaceConsoleHandler(); // actually not "replace" but "add"

try {
Thread.sleep(10000); // You have 10 seconds to close console
} catch (InterruptedException e) {
}
}

public static void shutdown() {
hook.run();
}

private static native void replaceConsoleHandler();

static {
System.loadLibrary("TestConsoleHandler");
}
}

class ShutdownHook extends Thread {
public void run() {
try {
// do some visible work
new File("d:/shutdown.mark").createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Shutdown");
}
}


执行javah TestConsoleHandler来获取TestConsoleHandler.h。
在visual studio里新建c++项目 -> win32(dll)项目TestConsoleHandler,将TestConsoleHandler.h导入头文件,编写TestConsoleHandler.cpp如下:

#include "stdafx.h"
#include "TestConsoleHandler.h"

JavaVM *jvm;

BOOL WINAPI HandlerRoutine(__in DWORD dwCtrlType) {
if (dwCtrlType == CTRL_CLOSE_EVENT) {
JNIEnv *env;
jint res = jvm->AttachCurrentThread((void **)(&env), &env);
jclass cls = env->FindClass("TestConsoleHandler");
jmethodID mid = env->GetStaticMethodID(cls, "shutdown", "()V");
env->CallStaticVoidMethod(cls, mid);
jvm->DetachCurrentThread();
return TRUE;
}
return FALSE;
}

JNIEXPORT void JNICALL Java_TestConsoleHandler_replaceConsoleHandler(JNIEnv *env, jclass clazz) {
env->GetJavaVM(&jvm);
SetConsoleCtrlHandler(&HandlerRoutine, TRUE);
}

编译成dll拷贝到java项目根目录即可。

[size=large][b]JNA实现:[/b][/size]

import java.io.File;
import java.io.IOException;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;


public class TestJnaConsoleHandler {
private static Thread hook;
/**
* @param args
*/
public static void main(String[] args) {
System.out.println("Start");
hook = new ShutdownHook2();
Runtime.getRuntime().addShutdownHook(hook);
CLib lib = (CLib) Native.loadLibrary("TestJnaConsoleHandler", CLib.class);
lib.init(new CLib.OpenFunc() {
public void invoke(String name, int i) {
hook.run();
}
});
lib.replaceConsoleHandler();

try {
Thread.sleep(10000); // You have 10 seconds to close console
} catch (InterruptedException e) {
}
}
public interface CLib extends Library{
public interface OpenFunc extends Callback {
void invoke(String name, int i);
}
void init(OpenFunc openfunc);
void replaceConsoleHandler();
}
}

class ShutdownHook2 extends Thread {
public void run() {
try {
// do some visible work
new File("d:/shutdown.mark").createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Shutdown");
}
}



c++实现:

#include "stdafx.h"
#include <iostream>

using namespace std;

#define MYLIBAPI extern "C" __declspec( dllexport )


typedef void (*OpenFunc)(const char*,int);
OpenFunc openfunc = NULL;
MYLIBAPI void init(OpenFunc func) {
cout << "init called" <<endl;
openfunc = func;
}

BOOL WINAPI HandlerRoutine(__in DWORD dwCtrlType) {
if (dwCtrlType == CTRL_CLOSE_EVENT) {

(*openfunc)("test", 0);

return TRUE;
}
return FALSE;
}

MYLIBAPI void replaceConsoleHandler() {
SetConsoleCtrlHandler(&HandlerRoutine, TRUE);
}


至此,当用户关闭console窗口或是按下ctrl+C时,都会调用预定义的方法来优雅地处理。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值