《Mybatis源码》第20章 ErrorContext 全局异常信息

《Mybatis源码》第20章 ErrorContext 全局异常信息,第1张

《Mybatis源码》第20章 ErrorContext 全局异常信息

# ErrorContext 错误日志

mybatis在读取mapper文件的时候,会存在下面这行代码,那么它用到了ErrorContext类,那么该类的作用是啥?

ErrorContext.instance().resource(resource);

MyBatis 还有一个很有意思的点在于异常日志的输出。不知道大家有没有发现,使用 MyBatis 时定位问题非常容易,我们只需要查看一下控制台的异常日志就能一目了然地知道问题出现在了哪里。就像这样:

1.存储异常信息的字段

MyBatis 异常涵盖的信息总结为一点就是:异常是由谁在做什么的时候在哪个资源文件中发生的,执行的 SQL 是哪个,以及 java 详细的异常信息。这六个私有变量分别存储这些信息:

public class ErrorContext {  
  // 换行符,因为不同的系统,换行符可能不同,而该类的目的主要就是展示日志,
  private static final String LINE_SEPARATOR = System.getProperty("line.separator","n");
  private static final ThreadLocal LOCAL = new ThreadLocal();

  private ErrorContext stored;
  // 异常文件
  private String resource;
  // 异常 *** 作
  private String activity;
  // 异常对象
  private String object;
  // 异常信息
  private String message;
  // 异常sql
  private String sql;
  // 异常日志
  private Throwable cause;
  • resource:存储异常存在于哪个资源文件中。
    如:### The error may exist in mapper/AuthorMapper.xml
  • activity:存储异常是做什么 *** 作时发生的。
    如:### The error occurred while setting parameters
  • object:存储哪个对象 *** 作时发生异常。
    如:### The error may involve defaultParameterMap
  • message:存储异常的概览信息。
    如:### Error querying database. Cause: java.sql.SQLSyntaxErrorException: Unknown column ‘id2’ in ‘field list’
  • sql:存储发生日常的 SQL 语句。
    如:### SQL: select id2, name, sex, phone from author where name = ?
  • cause:存储详细的 Java 异常日志。
    如:### Cause: java.sql.SQLSyntaxErrorException: Unknown column ‘id2’ in ‘field list’ at

对于这六个成员变量的 “set” 方法,命名同相应成员变量,均是对成员变量做赋值 *** 作并返回存储完相应信息后当前 ErrorContext 的实例。例如:

 public ErrorContext resource(String resource) {
    this.resource = resource;
    return this;
  }

  public ErrorContext activity(String activity) {
    this.activity = activity;
    return this;
  }

  // 其余省略...
2.使用 ThreadLocal 管理 ErrorContext
private static final ThreadLocal LOCAL = new ThreadLocal();

ThreadLocal 是本地线程存储,它的作用是为变量在每个线程中创建一个副本,每个线程内部都可以使用该副本,线程之间互不影响。使用 ThreadLocal 来管理 ErrorContext,保证了在多线程环境中,每个线程内部可以共用一份 ErrorContext,但多个线程持有的 ErrorContext 互不影响,保证了异常日志的正确输出。

ErrorContext采用了单例模式,保证了每个线程共用一份实例

  // 私有构造方法
  private ErrorContext() {
  }
  // 静态方法获取实例
  public static ErrorContext instance() {
    ErrorContext context = LOCAL.get();
    if (context == null) {
      context = new ErrorContext();
      LOCAL.set(context);
    }
    return context;
  } 
3.其它方法

stored 变量充当一个中介,在调用store()方法时将当前 ErrorContext保存下来,在调用 recall() 方法时将该 ErrorContext 实例传递给 LOCAL。

  public ErrorContext store() {
    stored = this;
    LOCAL.set(new ErrorContext());
    return LOCAL.get();
  }

  public ErrorContext recall() {
    if (stored != null) {
      LOCAL.set(stored);
      stored = null;
    }
    return LOCAL.get();
  }

reset()顾名思义就是重置方法

public ErrorContext reset() {
    resource = null;
    activity = null;
    object = null;
    message = null;
    sql = null;
    cause = null;
    LOCAL.remove();
    return this;
  }

toString()方法就是显示我们的错误信息

@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();
  }
4.Mybatis使用

mybatis是如何使用的,其实这个就相当于是一个进度,例如上面的那行代码,就是把资源路径存入,然后在后面执行SQL的时候,会继续存入其它的信息

// 把加载的资源文件路径存入
ErrorContext.instance().resource(resource);

这里还有一个小细节,为啥这些赋值方法,要返回this当前这个对象,因为这样就更方便多个赋值,例如ErrorContext.instance().message(message).cause(e).toString(),这样赋值 *** 作1行就可以搞定

5.ErrorContext

text.instance().resource(resource);

这里还有一个小细节,为啥这些赋值方法,要返回`this`当前这个对象,因为这样就更方便多个赋值,例如`ErrorContext.instance().message(message).cause(e).toString()`,这样赋值 *** 作1行就可以搞定

## 5.ErrorContext

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/5605695.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-12-15
下一篇2022-12-15

发表评论

登录后才能评论

评论列表(0条)

    保存