Java: System.exit() 与安全策略
說明
System.exit() 的本質(zhì)是通知 JVM 關(guān)閉。
一般來說,有兩種禁用 System.exit() 的辦法:
- 安全管理器
- 安全策略
本質(zhì)都是JRE 提供的本地實現(xiàn),在執(zhí)行之前進(jìn)行權(quán)限判斷。
因為System.exit() 是一種很暴力的手段,如果在 Client 模式下自己寫個小程序無所謂,但是在 Server 上多個程序、或者多線程時就會有很大的麻煩。
底層源碼
1.先來看看靜態(tài)方法 System.exit() 的源碼:
// System.exit() public static void exit(int status) {Runtime.getRuntime().exit(status); }應(yīng)該說很簡單, 只是簡單地調(diào)用運(yùn)行時的 exit 方法.
2.然后我們看運(yùn)行時的實例方法 exit:
// Runtime.exit() public void exit(int status) {SecurityManager security = System.getSecurityManager();if (security != null) {security.checkExit(status);}Shutdown.exit(status); }如果有安全管理器,那么就讓安全管理器執(zhí)行 checkExit退出權(quán)限檢查。
如果檢查不通過,安全管理器就會拋出異常(這就是約定!)。
然后當(dāng)前線程就會往外一路拋異常,如果不捕獲,那么該線程就會退出。
此時如果沒有其他的前臺線程正在運(yùn)行,那么JVM也會跟著退出。
3.Shutdown 是 java.lang 包下面的一個類。
訪問權(quán)限是 default, 所以我們在API中是不能調(diào)用的。
// Shutdown.exit() static void exit(int status) {boolean runMoreFinalizers = false;synchronized (lock) {if (status != 0) runFinalizersOnExit = false;switch (state) {case RUNNING: /* Initiate shutdown */state = HOOKS;break;case HOOKS: /* Stall and halt */break;case FINALIZERS:if (status != 0) {/* Halt immediately on nonzero status */halt(status);} else {/* Compatibility with old behavior:* Run more finalizers and then halt*/runMoreFinalizers = runFinalizersOnExit;}break;}}if (runMoreFinalizers) {runAllFinalizers();halt(status);}synchronized (Shutdown.class) {/* Synchronize on the class object, causing any other thread* that attempts to initiate shutdown to stall indefinitely*/sequence();halt(status);} }其中有一些同步方法進(jìn)行鎖定。 退出邏輯是調(diào)用了 halt 方法。
// Shutdown.halt() static void halt(int status) {synchronized (haltLock) {halt0(status);} }static native void halt0(int status);然后就是調(diào)用 native 的 halt0() 方法讓 JVM “自殺“了。
示例
使用安全管理器的實現(xiàn)代碼如下所示:
1.定義異常類, 繼承自 SecurityException
ExitException.java
package com.cncounter.security;public class ExitException extends SecurityException {private static final long serialVersionUID = 1L;public final int status;public ExitException(int status) {super("忽略 Exit方法調(diào)用!");this.status = status;} }2.定義安全管理器類, 繼承自 SecurityManager
NoExitSecurityManager.java
package com.cncounter.security;import java.security.Permission;public class NoExitSecurityManager extends SecurityManager {@Overridepublic void checkPermission(Permission perm) {// allow anything.}@Overridepublic void checkPermission(Permission perm, Object context) {// allow anything.}@Overridepublic void checkExit(int status) {super.checkExit(status);throw new ExitException(status);} }其中直接拒絕系統(tǒng)退出。
3.增加一個輔助和測試類,實際使用時你也可以自己進(jìn)行控制。
NoExitHelper.java
package com.cncounter.security;public class NoExitHelper {/*** 設(shè)置不允許調(diào)用 System.exit(status)* * @throws Exception*/public static void setNoExit() throws Exception {System.setSecurityManager(new NoExitSecurityManager());}public static void main(String[] args) throws Exception {setNoExit();testNoExit();testExit();testNoExit();}public static void testNoExit() throws Exception {System.out.println("Printing works");}public static void testExit() throws Exception {try {System.exit(42);} catch (ExitException e) {//System.out.println("退出的狀態(tài)碼為: " + e.status);}} }在其中,使用了一個 main 方法來做簡單的測試。 控制臺輸出結(jié)果如下:
Printing works 退出的狀態(tài)碼為: 42 Printing works原問題
原來的問題如下:
I’ve got a few methods that should call System.exit() on certain inputs. Unfortunately, testing these cases causes JUnit to terminate! Putting the method calls in a new Thread doesn’t seem to help, since System.exit() terminates the JVM, not just the current thread. Are there any common patterns for dealing with this? For example, can I subsitute a stub for System.exit()?
大意是:
有一些方法需要測試, 但是在某些特定的輸入時就會調(diào)用 System.exit()。這就杯具了,這時候 JUnit 測試也跟著退出了! 用一個新線程來調(diào)用這種方法也沒什么用, 因為 System.exit() 會停止JVM , 而不是退出當(dāng)前線程。有什么通用的模式來處理這種情況嗎? 例如,我能替換掉 System.exit() 方法嗎?
建議如下:
Instead of terminating with System.exit(whateverValue), why not throw an unchecked exception? In normal use it will drift all the way out to the JVM’s last-ditch catcher and shut your script down (unless you decide to catch it somewhere along the way, which might be useful someday).
In the JUnit scenario it will be caught by the JUnit framework, which will report that such-and-such test failed and move smoothly along to the next.
翻譯如下:
在程序中調(diào)用 System.exit(whateverValue) 是一種很不好的編程習(xí)慣, 這種情況為什么不拋出一個未檢測的異常(unchecked exception)呢? 如果程序中不進(jìn)行捕獲(catch), 拋出的異常會一路漂移到 JVM , 然后就會退出程序(只有主線程的話)。
在 JUnit 的測試場景中異常會被 JUnit 框架捕獲, 然后就會報告說某某某測試執(zhí)行失敗,然后就繼續(xù)下一個單元測試了。
當(dāng)然,給出的解決方案就是前面的那段代碼. 你還可以閱讀下面的參考文章,查找其他的解決方案。
參考文章:
Java: 如何單元測試那種會調(diào)用 System.exit() 的方法?
Java中如何禁止 API 調(diào)用 System.exit()
如何配置 Tomcat 的安全管理器
日期: 2015年08月25日
人員: 鐵錨 http://blog.csdn.net/renfufei
總結(jié)
以上是生活随笔為你收集整理的Java: System.exit() 与安全策略的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ioremap 与 mmap【转】
- 下一篇: php+mysqli实现批量执行插入、更