異常管理(java)

前言

程序運行時出現異常是很常見的,有時候在本地測試都沒問題,一旦部署到生產服務器運行一段時間就出現問題。這個時候,我們就應該在代碼中對可能產生異常的地方(常見的對數據庫的操作)編寫異常處理的代碼,或是響應給客戶端,或是將異常記錄到日誌(比如log4j),然後在進行處理。本文主要講的是個人對 try catch 和throws對異常的處理認識。

對try catch的認識

這個常用的是try{code}catch(){}來對try裏面的代碼進行檢查,如果運行時出現異常,就會進入進入catch 裏面的代碼,那麼有個小問題,當catch 完之後,程序是否有返回值(如果這個方法有返回值),項目是否會繼續運行??

我們先來看下面的測試代碼:

public class TestTryCatch {

    public static int myTest(){
        int i = 0;
        try {
            int x = 5;
            int y = 0;
            int z = x/y;
        } catch (Exception e) {
         i= 1;
        e.printStackTrace();
        }
        return i;
    }

    public static void main(String[] args) {
        int i = myTest();
        System.out.println("i="+i);
    }

}

對於這個簡單的例子,在不放進編輯器運行的時候,你覺得i的值會是什麼??下面有三種選擇:A、0 B、1 C、程序停止,沒有返回值。
最終運行結果是:B 1 。

這個簡單的例子,其實跟我之前的認識是有區別的。我一直認爲當程序遇到異常,也就是try catch 之後,程序不會有返回值,程序在catch 之後會停止,但是很明顯我的認識是錯誤的。其實這裏在編碼的時候,如果是我們以我的認識,那麼將會導致一個bug,請看下面的例子:比如說有個送流量的活動,每次輸入號碼的時候要先查詢是否領取,如果領取了將不能再領取。

public static  String checkUserTraffic(String number,String returnCode,String nowTime) {

            String flow = null;
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try {
                String sql = "select  flow  from FLOW_INFO where PHONE = ? and RETURNCODE = ? and RECIEVETIME = ?";
                conn = GreenetHomeConnecFactory.getConnection();
                ps = conn.prepareStatement(sql);
                ps.setString(1, number);
                ps.setString(2, returnCode);
                ps.setString(3, nowTime);
                rs = ps.executeQuery();
                while (rs.next()) {
                    flow = rs.getString("FLOW");
                    System.out.println("flow:" + flow);
                }
            } catch (SQLException e) {
                Log.error("查詢是否領取流量失敗,參數"+"number="+number+",returnCode="+returnCode+",nowTime="+nowTime+"異常信息:"+e.toString());
            } finally {
                // conn.setAutoCommit(true);
                GreenetHomeConnecFactory.close(conn, ps, rs);
            }

            return flow;
        }

大家有沒有看出代碼中隱藏的bug?沒錯,就是沒對異常做出正確的處理,這裏對異常處理只打印了輸入參數。但是你發現沒有,如果程序遇到異常,或者數據庫不存在那個用戶的記錄,flow都會返回null;比如說當用戶輸入已經領取過流量的號碼時,理論上是不能在領取的,但是這時候程序出現異常,返回的是null,那麼他將會成功的再領一次流量,天大的漏洞啊。所以在這裏有個比較的做法是,在catch這裏給flow賦值,比如說給個error,然後控制器可以知道遇到了異常,直接將結果返回,不需要再繼續下去,這樣可以避免損失。

404找不到頁面處理

這個其實比較簡單,做個用於展示404的頁面,然後在web.xml配置即可。

   <!--  設置錯誤頁面 -->
<error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/views/error/404.jsp</location>
    </error-page>

全局異常處理器

其實這個纔是我想說的重點。關於全局異常處理器,很多博客都會有介紹,我要說的是完善全局異常的輸出信息。

首先全局異常器是在springmvc中配置的

   <bean id="resolveExceptionclass="com.aotain.read.controller.ExceptionHandlerController"/>

然後當遇到異常的時候,正常來說,如果沒有try catch處理,你可以寫throws 或者不寫,他都會進入異常處理器。下面是處理器的代碼:

public class ExceptionHandlerController implements HandlerExceptionResolver{

    private static Logger Log = Logger.getLogger("sysLog");

    public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) {
        //轉發到異常頁面並輸出打印日誌(對於異步請求不會轉發頁面)
        ModelAndView mav = new ModelAndView("error/500");
        Log.error("異常信息:"+ex);
        return mav;
    }
}

不知道大家有沒有發現個問題,這裏對異常的處理不是很好,返回一個視圖告知用戶,還行;但是沒把具體出異常的堆棧信息(具體是什麼異常,在那些代碼中出現這個異常)打印出來,這對於我們發現和解決問題造成一定的困難,因爲出現異常的時候,單憑簡單的異常類型就去主觀臆斷,不是很好。
所以我就去百度了下Exception 都有哪些方法。
一開始我發現的就是這三個:

返回類型        方法                    結果
String         getMessage()     異常的類型
String        ex.toString()         異常的類型
               printStackTrace()    完整的堆棧信息

第三個方法的結果就是我們需要的,但是返回類型爲void,但是其它兩個方法又不夠具體。後來我看到了這個博客Java 打印堆棧的幾種方法,我從這裏找到了答案,修改了寫法

/**
 * 全局異常捕捉器
 * @author huangjg
 *2017年10月27日
 *
 */
public class ExceptionHandlerController implements HandlerExceptionResolver{

    private static Logger Log = Logger.getLogger("sysLog");
    public ModelAndView resolveException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) {

        ModelAndView mav = new ModelAndView("error/500");//轉發到異常頁面並輸出打印日誌(對於異步請求不會轉發頁面)
        StackTraceElement[] sTraceElements = ex.getStackTrace();
        Log.error("異常信息:"+ex.getMessage());
        for (int i = 0; i < sTraceElements.length; i++) {
            Log.error("異常位置:"+sTraceElements[i].getClassName()+"."+sTraceElements[i].getFileName()+"."+sTraceElements[i].getMethodName()+":"+sTraceElements[i].getLineNumber());
            }

        return mav;
    }
}

通過遍歷sTraceElements和log4j的結合,一方面可以記錄出現異常的堆棧信息,一方面也可以也可以給客戶端一個響應,並且我做測試的時候,不會像try catch一樣會有返回值(如果有返回值),而是直接進入了異常機制,這樣程序不會照着正常的流程走下去。不過這裏要注意的是,如果異常被你try catch到的,就會進入catch 處理,就算你配置了全局異常處理器,也不會進入異常處理器那裏。但是我也發現個小問題,如果是客戶端發情ajax異步請求,也就是說出現異常的時候不會發生跳轉,這樣就無法通知客戶端唉,這裏還沒找到合理的解決方法。

結語

以上是我對異常管理簡單的認識,如果有朋友發現我寫的不好或者有什麼好的意見的都可以給我留言,大家交流交流。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章