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有必要考虑这个问题吗?或许真的有必要吧,谁让它有那么多成员变量呢!
不求甚解,但套路必须要学!!!
想想自己项目中的异常体系,简直不堪入目!!!