1. 程式人生 > 實用技巧 >SSM框架全域性捕獲異常

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就可以認為伺服器端資料處理失敗。至於具體的錯誤資訊需不需要返回前端可以根據業務決定。