Mybatis源码分析(1)

Mybatis源码分析(1)

1、Maven

什么是OGNL?

OGNL的三要素

  表达式:OGNL表达式是功能强大的表达式语言,何解?在ognl中想要执行取值,赋值,调用方法等等操作,你都需要用表达式表示。通过表达式,底层会解析出来你的想要操作。它支持链式结构

  根对象:即root对象,可以理解为OGNL的操作对象,表达式规定做什么,而该对象就指定对谁操作。OGNL叫做对象图导航语言,对象图就是以任意一个对象作为根,通过OGNL可以访问到与这个对象相关的其他对象。底层使用list集合做的。

  Context对象:其实就是OGNL的上下文环境。root对象也在OGNL的上下文环境里,底层是一个Map集合。该上下文环境规定了OGNL操作在“哪里进行”,注意访问context对象时候需要在表达式中加上#。

JAVAssist

Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

reload4j

核心内容如下:

  1. log4j的1.2版本是一个通用版本,但是由于2022年的log4j漏洞原因,slf4j-log4j模块在build时,会自动重定向至slf4j-reload4j模块。
  2. 如果你想继续使用log4j 1.x的框架,强烈推荐你使用slf4j-reload4j进行替代。

cglib

基于类的动态代理

HSQLDB

Hsqldb是一个开放源代码的JAVA数据库,其具有标准的SQL语法和JAVA接口,它可以自由使用和分发,非常简洁和快速的

Apache Derby

Apache Derby是一个完全用java编写的数据库

h2

H2是一个短小精干的嵌入式数据库引擎,主要的特性包括: 免费、开源、快速 嵌入式的数据库服务器,支持集群 提供JDBC、ODBC访问接口

什么是Mockito

Mockito是一个非常优秀的模拟框架,可以使用它简洁的API来编写漂亮的测试代码,它的测试代码可读性高同时会产生清晰的错误日志。

velocity简介

Velocity是一个基于Java的模板引擎,可以通过特定的语法获取在java对象的数据 , 填充到模板中,从而实现界面和java代码的分离!

PostgreSQL

PostgreSQL是一种特性非常齐全的自由软件的对象-关系型数据库管理系统(ORDBMS)

mysql-connector-java

java连接mysql数据库

MySql

MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一

2、从哪里开始?

SqlSessionFactoryBuilder

用于通过字节流|字符流构建 SqlSessionFactory

内部构建过程:

  • 通过流获取相应的XMLConfigBuilder
  • XMLConfigBuilder解析返回Configuration
  • 根据配置构建出SqlSessionFactory
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
  try {
    XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
    return build(parser.parse());
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    ErrorContext.instance().reset();
    try {
     if (reader != null) {
       reader.close();
     }
    } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
    }
  }
}

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情

SqlSessionFactory

通过一些配置去创建SqlSession,例如:autoCommit|Connection|TransactionIsolationLevel|ExecutorType

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

使用 MyBatis 的主要 Java 接口。通过此接口,您可以执行命令、获取映射器和管理事务

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

// 以下非全部方法,只是作为举例
public interface SqlSession extends Closeable {
		<T> T selectOne(String statement, Object parameter);
	  <E> List<E> selectList(String statement, Object parameter);
	  <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
		<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);
	  <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);
	  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);
	  int insert(String statement, Object parameter);
	  int update(String statement, Object parameter);
	  int delete(String statement, Object parameter);
	  void commit(boolean force);
	  void rollback(boolean force);
	  <T> T getMapper(Class<T> type);
	  Connection getConnection();
}

绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession,如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:

try (SqlSession session = sqlSessionFactory.openSession()) {
  // 你的应用逻辑代码
}

所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。

3、缓存

Cache

Cache:缓存的顶级接口,定义了缓存的增删改查、获取大小,id等行为

PerpetualCache:在MyBatis里面,它是一级缓存、二级缓存的最基本实现 (详细参见),一下是所有特性的缓存模式均在包:org.apache.ibatis.cache.decorators 下:

BlockingCache:读阻塞缓存,对应的key没有被缓存的话,将会阻塞所有需要获取该key数据(也可以设定超时时间)的线程,直到有其他线程在缓存中设置该key数据后,阻塞线程将继续执行后续

FifoCache:固定大小队列缓存,维护了一个队列,可以设置队列大小(默认1024个位置),如果新进来一个key值,使得缓存集合中长度大于设定的长度,则将根据先进先出(first in, first out)原则,删除最先进来的key-value键值对儿,此队列只与put方法超过长度时有关,删除缓存元素不影响队列中的元素

LoggingCache:记录缓存命中率日志缓存,获取缓存的命中率日志(命中缓存key的个数与请求缓存总数的比率)

LruCache:最近最少使用的缓存,内部维护了一个LinkedHashMap,通过removeEldestEntry方法找到最近最老的key进行删除

ScheduledCache:定时清空缓存,在增删改查入口做了校验,该校验用于检查定时时间是否超过设定预期时间,超过则清空缓存数据

SerializedCache:序列化缓存,将key对应的value值以对象流的形式存于缓存中

SoftCache:软引用缓存,每次操作都会对应的将原来已被垃圾收集器收集过的对象数据删除,put操作会put一个软引用数据缓存

SynchronizedCache:同步缓存,内部几乎所有方法均加锁,是一个线程安全的缓存

TransactionalCache:事务性缓存,内部有两个方法,commiit以及rollback,在没有commit之前将所有key-value键值对临时存于一个内部map中,commit之后,将临时map以及get的时候的key空数据存储与真正的缓存中

WeakCache:若引用缓存,没什么可解释的

TransactionalCacheManager

让每一种特性的缓存都实现了事务的功能

内部维护了一个map

private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();

put是为传入的类型的缓存设置数据,在commit提交方法时,对每中特性缓存实例中的数据轮循做一次提交

public void putObject(Cache cache, CacheKey key, Object value) {
  getTransactionalCache(cache).putObject(key, value);
}
private TransactionalCache getTransactionalCache(Cache cache) {
    return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
}
public void commit() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.commit();
    }
}
public void rollback() {
    for (TransactionalCache txCache : transactionalCaches.values()) {
      txCache.rollback();
    }
}

CacheBuilder

构造缓存用的,内部维护了一个implementation(默认是:PerpetualCache),decorators(默认只有一个LruCache)

public class CacheBuilder {
  private final String id;
  private Class<? extends Cache> implementation; // 构造缓存时,基于该缓存实例构造的(将该缓存类型传入所有装饰者的构造函数中)
  private final List<Class<? extends Cache>> decorators;// 缓存类型的集合
  private Integer size;// 设置缓存大小(像fifo缓存以及Lru算法缓存需要根据size这个去判断是否需要删除元素)
  private Long clearInterval; // 设置清理间隔(如果有定时清空类型的缓存时)
  private boolean readWrite; // 暂时没有用到,Cache顶级类中有一个获取读写锁的方法,可能与这个有关
  private Properties properties;// 根据这个属性去填充各个类型缓存的字段数据
  private boolean blocking;// 是否阻塞(阻塞式缓存用到的)

  public CacheBuilder(String id) {
    this.id = id;
    this.decorators = new ArrayList<>();
  }
}

总结:这里Mybatis在缓存的设计上用了装饰器模式、抽象工厂模式

posted @ 2022-06-05 15:35  码出新生活!  阅读(64)  评论(0编辑  收藏  举报