栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

《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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/600891.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号