SSM框架全域性捕獲異常
我們都知道,專案有兩種異常,一種是ERROR,一種是Exception,ERROR導致專案直接崩盤,無法執行,且不能捕獲,Exception可以捕獲且不影響專案執行,今天要做的就是捕獲Exception,當前專案開發使用SSM框架,我原本使用的方法是Controller控制層每一個類每一個方法都有一個try-catch捕獲當前方法異常,雖然這種可用,但是會有幾個問題:
向前端如何返回異常?
即如何保證發生異常時向前端返回的資料統一,因為是不同人員開發,很可能A人員直接將異常扔給前端,而B人員將異常處理之後返回前端一個標識
如何記錄異常資訊?
我們要考慮到系統中每一處程式碼都有發生異常的可能性(在這裡先僅考慮Controller層),即使我們編寫異常記錄的工具類,難道我們在每處發生異常的地方都呼叫嗎?(不談資源浪費的情況下,如何確保每個開發人員記錄格式相同?如何確保每處Exception都會被記錄?)
基於以上兩點,我基於SSM框架做了一個Exception的全域性異常捕獲,郵件提醒(親測有效)
在這裡即使用GlobalExceptionHandler類捕獲控制層的異常,程式碼如下:
package com.single.cong.exception; import java.io.PrintWriter; import java.io.StringWriter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.security.access.AccessDeniedException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import lombok.extern.slf4j.Slf4j; /** * 全域性捕獲異常 */ @Slf4j @ControllerAdvice(basePackages = "com.single.cong.controller") public class GlobalExceptionHandler { @Autowired private JavaMailSender javaMailSender; /** * 異常資訊轉化為String型別,等同於e.printStackTrace()輸出引數 * * @param t * 異常 * @return 異常詳細資訊 * @author single-聰 * @date 2019年4月15日 * @version 1.0.0 */ public String getTrace(Throwable t) { StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); t.printStackTrace(writer); StringBuffer buffer = stringWriter.getBuffer(); SimpleMailMessage mailMessage = new SimpleMailMessage(); mailMessage.setFrom("[email protected]"); mailMessage.setTo("[email protected]"); mailMessage.setSubject("系統Bug,及時修復"); mailMessage.setText(buffer.toString()); javaMailSender.send(mailMessage); return buffer.toString(); } /** * 全域性異常捕獲,暫時區分兩種異常型別,所有執行時異常統一在此方法中 * * @param e * 異常 * @author single-聰 * @date 2019年4月15日 * @version 1.0.0 * */ @ExceptionHandler(RuntimeException.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) public void runtimeException(Exception e) { log.info("全域性捕獲執行時異常,同時寫入日誌檔案"); e.printStackTrace(); log.error(getTrace(e)); } /** * 賬號許可權資訊不足 * * @param e * 異常 * @author single-聰 * @date 2019年4月15日 * @version 1.0.0 * */ @ExceptionHandler(AccessDeniedException.class) @ResponseStatus(value = HttpStatus.UNAUTHORIZED) public void accessDeniedException(Exception e) { log.info("賬號許可權資訊不足"); e.printStackTrace(); log.error(getTrace(e)); } }
@ControllerAdvice註解將作用在所有註解了@RequestMapping的控制器的方法上,basePackages代表指定包下面的方法,可以使用不同的類處理不同的異常。
@ExceptionHandler(RuntimeException.class)用於全域性處理控制器裡的異常,在這裡我們只處理執行時異常以及許可權驗證異常。
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)返回狀態碼
引數裡面加上Exception之後就可以使用e.printStackTrace()在控制檯打印出異常,在這裡將打印出的異常儲存進日誌檔案(SpringBoot使用logback)方便檢視,這樣我們就不需要在每個控制層方法裡面寫多餘的程式碼了。
注意,郵件傳送部分需要更換成自己的賬號資訊,同時需要配置key。
在使用這個全域性方法之前,我的控制層程式碼如下:
// 刪除 @RequestMapping(value = "/delete.sose", consumes = "application/json;charset=UTF-8") public Map<String, Object> delete(@RequestBody String c) throws ParseException { java.util.Map<String, Object> map = new HashMap<>(); try { // 獲取前端傳回刪除資料Id JSONObject strj = new JSONObject(c); evolveService.delete(strj.getInt("id")); map.put("info", "success"); } catch (Exception e) { e.printStackTrace(); map.put("info", e); } return map; }
使用異常捕獲機制之後,只要你能夠將系統中的異常羅列出來,那麼就可以根據異常型別決定返回指定的狀態碼,前端只處理狀態碼為200的資料,一旦狀態碼不是200就可以認為伺服器端資料處理失敗。至於具體的錯誤資訊需不需要返回前端可以根據業務決定。