MyBatis框架定義了許多的異常類,之所以定義這么多的異常類,應該是將每一種異常情況都獨立出來,這樣在出現異常時,定位就很明確了。而我們平時寫代碼時,都是瞎搞一通,異常類大多也是隨便定義,或者是使用JDK自帶異常類,有時候甚至是直接弄Exception。缺點顯而易見了。今后寫代碼,也應該是學着MyBatis,將每種業務(或場景)可能出現的異常進行單獨封裝,這樣,當項目報錯,出現異常時,定位起來也就簡單很多。
MyBatis的異常爹是IbatisException.class ,而它又是RuntimeException的兒子。這是為什么呢?
1. Exception異常,要么拋,要么處理。。。強制性要求。。。
2. RuntimeException異常,編譯時我也不知道會不會出錯,運行時才知道結果咋樣,,,, 可以不處理,但是出錯了,程序就停擺。
具體啥原因,我也不知道,但是人家MyBatis都是這樣搞,那我們以后寫代碼也跟着這樣弄吧!!!
public class IbatisException extends RuntimeException { private static final long serialVersionUID = 3880206998166270511L; public IbatisException() { super(); } public IbatisException(String message) { super(message); } public IbatisException(String message, Throwable cause) { super(message, cause); } public IbatisException(Throwable cause) { super(cause); } }
MyBatis異常爹就是這樣定義的,提供了4個結構函數,構造函數全是super, 而且它所有的異常子類,似乎也都是這種款式。 以后工作中也跟着用唄!
工作中,我們也應該學習MyBatis的這種套路,但是這些並非MyBatis異常模塊的核心。在MyBatis中,它提供了兩個核心類,ExceptionFactory.class 和 ErrorContext.class。
其作用也是顧名思義。接着我們來看看它的具體實現,還是比較有意思的,以后工作中也應該嘗試這種套路。
1. ExceptionFactory.class
public class ExceptionFactory { private ExceptionFactory() { // Prevent Instantiation } public static RuntimeException wrapException(String message, Exception e) { return new PersistenceException(ErrorContext.instance().message(message).cause(e).toString(), e); } }
私有化構造器,但然后通過靜態方法,將異常信息和異常真實對象創建出來。
注意: 返回的異常類型是RuntimeException.class, 而 new 的是PersistenceException.class。 在高版本的MyBatis中將IbatisException.class標注為過時類,而 PersistenceException.class卻又是IbatisException.class的子類。。。。。 這似乎是一種代碼擴展/升級/兼容老版本的方式呢!!!!
不過,我覺得ExceptionFactory.class中,最關鍵是應該是ErrorContext.instance().message(message).cause(e).toString()這句代碼了。 下面我們來看看ErrorContext.class
2. ErrorContext.class
public class ErrorContext { private static final String LINE_SEPARATOR = System.getProperty("line.separator","\n"); private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>(); private ErrorContext stored; private String resource; private String activity; private String object; private String message; private String sql; private Throwable cause; private ErrorContext() { } public static ErrorContext instance() { ErrorContext context = LOCAL.get(); if (context == null) { context = new ErrorContext(); LOCAL.set(context); } return context; } public ErrorContext store() { ErrorContext newContext = new ErrorContext(); newContext.stored = this; LOCAL.set(newContext); return LOCAL.get(); } public ErrorContext recall() { if (stored != null) { LOCAL.set(stored); stored = null; } return LOCAL.get(); } public ErrorContext resource(String resource) { this.resource = resource; return this; } public ErrorContext activity(String activity) { this.activity = activity; return this; } public ErrorContext object(String object) { this.object = object; return this; } public ErrorContext message(String message) { this.message = message; return this; } public ErrorContext sql(String sql) { this.sql = sql; return this; } public ErrorContext cause(Throwable cause) { this.cause = cause; return this; } public ErrorContext reset() { resource = null; activity = null; object = null; message = null; sql = null; cause = null; LOCAL.remove(); return this; } @Override public String toString() { StringBuilder description = new StringBuilder(); // message if (this.message != null) { description.append(LINE_SEPARATOR); description.append("### "); description.append(this.message); } // resource if (resource != null) { description.append(LINE_SEPARATOR); description.append("### The error may exist in "); description.append(resource); } // object if (object != null) { description.append(LINE_SEPARATOR); description.append("### The error may involve "); description.append(object); } // activity if (activity != null) { description.append(LINE_SEPARATOR); description.append("### The error occurred while "); description.append(activity); } // activity if (sql != null) { description.append(LINE_SEPARATOR); description.append("### SQL: "); description.append(sql.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ').trim()); } // cause if (cause != null) { description.append(LINE_SEPARATOR); description.append("### Cause: "); description.append(cause.toString()); } return description.toString(); } }
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<>(); 因為少見多怪,所以看到這行代碼 ,瞬間就覺得有點逼 格了,ThreadLocal這個類的使用,一般都是為了解決線程安全的問題。MyBatis有必要考慮這個問題嗎?或許真的有必要吧,誰讓它有那么多成員變量呢!
不求甚解,但套路必須要學!!!
想想自己項目中的異常體系,簡直不堪入目!!!
