無聊系列 – 教你怎麼正確處理異常
- 2020 年 4 月 8 日
- 筆記
在工作中,常遇見亂處理Exception的情況:
- 要麼吞掉異常,不打印任何日誌;
- 要麼記錄日誌時,日誌級別不對、或者把重要的出錯堆棧信息幹掉,在做生產問題排查時,簡直讓人抓狂。
我這篇博文,也是對記錄的一個開源組件,對異常自行K掉,造成我排查耗費了好久的時間–。https://www.cnblogs.com/chongsha/p/11931109.html
下面我們用一段代碼對1進行舉例,該代碼是網上隨便搜的,原作者請勿見怪。
1 /* 2 * 加密 3 * 1.構造密鑰生成器 4 * 2.根據ecnodeRules規則初始化密鑰生成器 5 * 3.產生密鑰 6 * 4.創建和初始化密碼器 7 * 5.內容加密 8 * 6.返回字符串 9 */ 10 public static String AESEncode(String encodeRules,String content){ 11 try { 12 //1.構造密鑰生成器,指定為AES算法,不區分大小寫 13 KeyGenerator keygen=KeyGenerator.getInstance("AES"); 14 //2.根據ecnodeRules規則初始化密鑰生成器 15 //生成一個128位的隨機源,根據傳入的位元組數組 16 keygen.init(128, new SecureRandom(encodeRules.getBytes())); 17 //3.產生原始對稱密鑰 18 SecretKey original_key=keygen.generateKey(); 19 //4.獲得原始對稱密鑰的位元組數組 20 byte [] raw=original_key.getEncoded(); 21 //5.根據位元組數組生成AES密鑰 22 SecretKey key=new SecretKeySpec(raw, "AES"); 23 //6.根據指定算法AES自成密碼器 24 Cipher cipher=Cipher.getInstance("AES"); 25 //7.初始化密碼器,第一個參數為加密(Encrypt_mode)或者解密解密(Decrypt_mode)操作,第二個參數為使用的KEY 26 cipher.init(Cipher.ENCRYPT_MODE, key); 27 //8.獲取加密內容的位元組數組(這裡要設置為utf-8)不然內容中如果有中文和英文混合中文就會解密為亂碼 28 byte [] byte_encode=content.getBytes("utf-8"); 29 //9.根據密碼器的初始化方式--加密:將數據加密 30 byte [] byte_AES=cipher.doFinal(byte_encode); 31 //10.將加密後的數據轉換為字符串 32 //這裡用Base64Encoder中會找不到包 33 //解決辦法: 34 //在項目的Build path中先移除JRE System Library,再添加庫JRE System Library,重新編譯後就一切正常了。 35 String AES_encode=new String(new BASE64Encoder().encode(byte_AES)); 36 //11.將字符串返回 37 return AES_encode; 38 } catch (NoSuchAlgorithmException e) { 39 e.printStackTrace(); 40 } catch (NoSuchPaddingException e) { 41 e.printStackTrace(); 42 } catch (InvalidKeyException e) { 43 e.printStackTrace(); 44 } catch (IllegalBlockSizeException e) { 45 e.printStackTrace(); 46 } catch (BadPaddingException e) { 47 e.printStackTrace(); 48 } catch (UnsupportedEncodingException e) { 49 e.printStackTrace(); 50 } 51 52 //如果有錯就返加nulll 53 return null; 54 }
該段代碼主要的問題是:
- 吃掉了異常,因為是公共類,連日誌記錄都沒有
- 出現異常後,仍然返回了一個null值。
這個方法在我們平時使用時,如果不讀源碼,直接使用,第一直覺是,返回正確結果,如果不正確,那就會拋出異常。但是這段代碼卻返回了null,使用者遇到時,會抓狂,這是什麼情況啊,為啥不對,明明沒有報錯,萬般無奈,進代碼一看。。。原來是把異常給幹掉了。
對此代碼做出的改進建議是:
- 在方法上聲明throws是
- 如果你覺得1方案不爽,可以直接一個大的catch Exception,然後throw new RuntimeException(e.getMessage(), e);
- 出錯了就是出錯了,不能把錯誤自己幹掉,然後返回一個null。
要麼記錄日誌時,日誌級別不對、或者把重要的出錯堆棧信息幹掉,在做生產問題排查時,簡直讓人抓狂。
在用log4j記錄日誌時,請正確使用logger.error()來記錄日誌,請注意該方法的重載,不要使用 Exception的getMessage()方法只記錄異常的消息,而把異常的錯誤堆棧給拋棄,異常的錯誤堆棧是很有用的信息,會告訴你在哪行代碼出錯了,這樣你可以快速的定位錯誤。
1 package com.demo; 2 3 public class Test { 4 5 public static void main(String[] args) { 6 try { 7 int a = 0; 8 int b = 1; 9 10 System.out.println(b / a); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } 14 } 15 16 }
這段代碼的錯誤堆棧信息:
java.lang.ArithmeticException: / by zero
at com.demo.Test.main(Test.java:10)
這行錯誤信息at com.demo.Test.main(Test.java:10)標明了出錯位置,可以快速定位是在什麼地方。所以在記錄日誌的時候,請不要把錯誤堆棧信息幹掉了。