mybatis专题(三)-----mybatis源码学习

源码分析概述

源码包分析

下载地址

MyBatis 源码下载地址:https://github.com/mybatis/mybatis-3

导入过程

1. 下载MyBatis的源码

2. 检查maven的版本,必须是3.25以上,建议使用maven的最新版本

3. mybatis的工程是maven工程,在开发工具中导入,工程必须使用jdk1.8以上版本;

4. 把mybatis源码的pom文件中true,全部改为false,或者直接删除这行;

5. 在工程目录下执行 mvn clean install -Dmaven.test.skip=true,将当前工程安装到本地仓库(pdf插件报 错的话,需要将这个插件屏蔽);

6. 其他工程依赖此工程

其实就是把mybatis源码install本地仓库中,然后在自己的项目中去依赖mybatis源码工程。

mybatis整体架构

谈谈设计模式的几个原则

单一职责原则:一个类或者一个接口只负责唯一项职责,尽量设计出功能单一的接口;

依赖倒转原则:高层模块不应该依赖低层模块具体实现,解耦高层与低层。既面向接口编程,当实现发生变化 时,只需提供新的实现类,不需要修改高层模块代码;

开放-封闭原则:程序对外扩展开放,对修改关闭;换句话说,当需求发生变化时,我们可以通过添加新模块 来满足新需求,而不是通过修改原来的实现代码来满足新需求;

日志模块分析

基础支撑层源码分析

  • MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各 不相同,二MyBatis统一提供了trace、debug、warn、error四个级别;
  • 自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog;
  • 日志的使用要优雅的嵌入到主体功能中;

适配器模式

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁,将一个类的接口转换成客户希望的 另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作;

解释如下:

姚明刚去NBA打球,英文不行,队友也听不懂他说啥,于是请了个翻译,翻译从中起到桥梁的作用,将姚明说的话适配给队友以及教练听,这样才能愉快的打球!

适用场景:当调用双方都不太容易修改的时候,为了复用现有组件可以使用适配器模式;在系统中接入第三方组 件的时候经常被使用到;

注意:如果系统中存在过多的适配器,会增加系统的复杂性,设计人员应考虑对系统进行重构;

日志模块类图

代理模式那些事

定义:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用;

目的:

  • (1)通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的 不必要复杂性;
  • (2)通过代理对象对原有的业务增强;

代理模式给我们带来的便利

  • 作为中介解耦客户端和真实对象,保护真实对象安全;(房屋中介)
  • 防止直接访问目标对象给系统带来的不必要复杂性;(海外代购,SSH)
  • 对业务进行增强,增强点多样化如:前入、后入、异常;(AOP)

代理模式类图

动态代理图示

解密动态代理

静态代理和动态代理的区别

1、静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。

2、静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。

3、动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行

时,动态修改字节码达到修改类的目的。

日志模块JDBC包类图

数据源模块

  • 常见的数据源组件都实现了javax.sql.DataSource接口;
  • MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现;
  • 一般情况下,数据源的初始化过程参数较多,比较复杂;

工厂模式uml类图

工厂模式(Factory Pattern)属于创建型模式,它提供了一种创建对象的最佳方式。定义一个创建对象的接 口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行

工厂接口(Factory):工厂接口是工厂方法模式的核心接口,调用者会直接和工厂接口交互用于获取具体的 产品实现类;

具体工厂类(ConcreteFactory):是工厂接口的实现类,用于实例化产品对象,不同的具体工厂类会根据需求 实例化不同的产品实现类;

产品接口(Product):产品接口用于定义产品类的功能,具体工厂类产生的所有产品都必须实现这个接口。 调用者与产品接口直接交互,这是调用者最关心的接口;

具体产品类(ConcreteProduct):实现产品接口的实现类,具体产品类中定义了具体的业务逻辑;

为什么要使用工厂模式?

创建对象的方式:

数据源模块类图

PooledConnection:使用动态代理封装了真正的数据库连接对象;

PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别 管理空闲状态的连接资源和活跃状态的连 接资源

PooledDataSource:一个简单,同步的、线程安全的数据库连接池

PooledDataSource 获取和归还连接过程

PooledDatasource源码

成员变量

//使用连接池的数据源
public class PooledDataSource implements DataSource {

  private static final Log log = LogFactory.getLog(PooledDataSource.class);

  private final PoolState state = new PoolState(this);

  //真正用于创建连接的数据源
  private final UnpooledDataSource dataSource;

  // OPTIONAL CONFIGURATION FIELDS
  //最大活跃连接数
  protected int poolMaximumActiveConnections = 10;
  //最大闲置连接数
  protected int poolMaximumIdleConnections = 5;
  //最大checkout时长(最长使用时间)
  protected int poolMaximumCheckoutTime = 20000;
  //无法取得连接是最大的等待时间
  protected int poolTimeToWait = 20000;
  //最多允许几次无效连接
  protected int poolMaximumLocalBadConnectionTolerance = 3;
  //测试连接是否有效的sql语句
  protected String poolPingQuery = "NO PING QUERY SET";
  //是否允许测试连接
  protected boolean poolPingEnabled;
  //配置一段时间,当连接在这段时间内没有被使用,才允许测试连接是否有效
  protected int poolPingConnectionsNotUsedFor;
  //根据数据库url、用户名、密码生成一个hash值,唯一标识一个连接池,由这个连接池生成的连接都会带上这个值
  private int expectedConnectionTypeCode;

  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }

  public PooledDataSource(UnpooledDataSource dataSource) {
    this.dataSource = dataSource;
  }
}

获取数据库连接

private PooledConnection popConnection(String username, String password) throws SQLException {
    boolean countedWait = false;
    PooledConnection conn = null;
    long t = System.currentTimeMillis();//记录尝试获取连接的起始时间戳
    int localBadConnectionCount = 0;//初始化获取到无效连接的次数

    while (conn == null) {
      synchronized (state) {//获取连接必须是同步的
        if (!state.idleConnections.isEmpty()) {//检测是否有空闲连接
          // Pool has available connection
          //有空闲连接直接使用
          conn = state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {// 没有空闲连接
          if (state.activeConnections.size() < poolMaximumActiveConnections) {//判断活跃连接池中的数量是否大于最大连接数
            // 没有则可创建新的连接
            conn = new PooledConnection(dataSource.getConnection(), this);
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } else {// 如果已经等于最大连接数,则不能创建新连接
            //获取最早创建的连接
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
            if (longestCheckoutTime > poolMaximumCheckoutTime) {//检测是否已经以及超过最长使用时间
              // 如果超时,对超时连接的信息进行统计
              state.claimedOverdueConnectionCount++;//超时连接次数+1
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;//累计超时时间增加
              state.accumulatedCheckoutTime += longestCheckoutTime;//累计的使用连接的时间增加
              state.activeConnections.remove(oldestActiveConnection);//从活跃队列中删除
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {//如果超时连接未提交,则手动回滚
                try {
                  oldestActiveConnection.getRealConnection().rollback();
                } catch (SQLException e) {//发生异常仅仅记录日志
                  /*
                     Just log a message for debug and continue to execute the following
                     statement like nothing happend.
                     Wrap the bad connection with a new PooledConnection, this will help
                     to not intterupt current executing thread and give current thread a
                     chance to join the next competion for another valid/good database
                     connection. At the end of this loop, bad {@link @conn} will be set as null.
                   */
                  log.debug("Bad connection. Could not roll back");
                }
              }
              //在连接池中创建新的连接,注意对于数据库来说,并没有创建新连接;
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
              conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
              //让老连接失效
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } else {
              // 无空闲连接,最早创建的连接没有失效,无法创建新连接,只能阻塞
              try {
                if (!countedWait) {
                  state.hadToWaitCount++;//连接池累计等待次数加1
                  countedWait = true;
                }
                if (log.isDebugEnabled()) {
                  log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
                }
                long wt = System.currentTimeMillis();
                state.wait(poolTimeToWait);//阻塞等待指定时间
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;//累计等待时间增加
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
        if (conn != null) {//获取连接成功的,要测试连接是否有效,同时更新统计数据
          // ping to server and check the connection is valid or not
          if (conn.isValid()) {//检测连接是否有效
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback();//如果遗留历史的事务,回滚
            }
            //连接池相关统计信息更新
            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            state.activeConnections.add(conn);
            state.requestCount++;
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else {//如果连接无效
            if (log.isDebugEnabled()) {
              log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
            }
            state.badConnectionCount++;//累计的获取无效连接次数+1
            localBadConnectionCount++;//当前获取无效连接次数+1
            conn = null;
            //拿到无效连接,但如果没有超过重试的次数,允许再次尝试获取连接,否则抛出异常
            if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) {
              if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Could not get a good connection to the database.");
              }
              throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
      }

    }

    if (conn == null) {
      if (log.isDebugEnabled()) {
        log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
      }
      throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }

    return conn;
  }

归还连接

//回收连接资源
  protected void pushConnection(PooledConnection conn) throws SQLException {

    synchronized (state) {//回收连接必须是同步的
      state.activeConnections.remove(conn);//从活跃连接池中删除此连接
      if (conn.isValid()) {
          //判断闲置连接池资源是否已经达到上限
        if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
            //没有达到上限,进行回收
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();//如果还有事务没有提交,进行回滚操作
          }
          //基于该连接,创建一个新的连接资源,并刷新连接状态
          PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
          state.idleConnections.add(newConn);
          newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
          newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
          //老连接失效
          conn.invalidate();
          if (log.isDebugEnabled()) {
            log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
          }
          //唤醒其他被阻塞的线程,因为获取连接过程中,有些线程是阻塞的
          state.notifyAll();
        } else {//如果闲置连接池已经达到上限了,将连接真实关闭
          state.accumulatedCheckoutTime += conn.getCheckoutTime();
          if (!conn.getRealConnection().getAutoCommit()) {
            conn.getRealConnection().rollback();
          }
          //关闭真的数据库连接
          conn.getRealConnection().close();
          if (log.isDebugEnabled()) {
            log.debug("Closed connection " + conn.getRealHashCode() + ".");
          }
          //将连接对象设置为无效
          conn.invalidate();
        }
      } else {
        if (log.isDebugEnabled()) {
          log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
        }
        state.badConnectionCount++;
      }
    }
  }

PoolState

/**
 * PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别
 * 管理空闲状态的连接资源和活跃状态的连接资源
 *
 * @author Clinton Begin
 */
public class PoolState {

  protected PooledDataSource dataSource;
  //空闲的连接池资源集合
  protected final List<PooledConnection> idleConnections = new ArrayList<>();
  //活跃的连接池资源集合
  protected final List<PooledConnection> activeConnections = new ArrayList<>();
  //请求的次数
  protected long requestCount = 0;
  //累计的获得连接的时间
  protected long accumulatedRequestTime = 0;
  //累计的使用连接的时间。从连接取出到归还,算一次使用的时间;
  protected long accumulatedCheckoutTime = 0;
  //使用连接超时的次数
  protected long claimedOverdueConnectionCount = 0;
  //累计超时时间
  protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
  //累计等待时间
  protected long accumulatedWaitTime = 0;
  //等待次数
  protected long hadToWaitCount = 0;
  //无效的连接次数
  protected long badConnectionCount = 0;

  public PoolState(PooledDataSource dataSource) {
    this.dataSource = dataSource;
  }
}

缓存模块

概述

装饰器模式是一种用于代替继承的技术,无需通过继承增 加子类就能扩展对象的新功能。使用对象的关联关系代替 继承关系,更加灵活,同时避免类型体系的快速膨胀。

装饰器模式uml类图

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,是一种用于代替继承的技术,无需通过继 承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀;

组件(Component):组件接口定义了全部组件类 和装饰器实现的行为;

组件实现类(ConcreteComponent):实现 Component接口,组件实现类就是被装饰器装饰的 原始对象,新功能或者附加功能都是通过装饰器添加到该类的对象上的;

装饰器抽象类(Decorator):实现Component接口的抽象类,在其中封装了一个Component 对象,也就是被装饰的对象;

具体装饰器类(ConcreteDecorator):该实现类 要向被装饰的对象添加某些功能;

装饰器模式使用图示

装饰器模式使用举例

1、IO中输入流和输出流的设计

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("c://a.txt")));

2、Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper, HttpServletRequestWrapper类增强了request对象的功能。

3、Mybatis的缓存组件

Cache:Cache接口是缓存模块的核心接口,定义了缓存的基 本操作;

PerpetualCache:在缓存模块中扮演ConcreteComponent 角色,使用HashMap来实现cache的相关操作;

BlockingCache:阻塞版本的缓存装饰器,保证只有一个线程 到数据库去查找指定的key对应的数据;

缓存源码分析

cache接口

public interface Cache {

  String getId();//缓存实现类的id

  void putObject(Object key, Object value);//往缓存中添加数据,key一般是CacheKey对象

  Object getObject(Object key);//根据指定的key从缓存获取数据

  Object removeObject(Object key);//根据指定的key从缓存删除数据

  void clear();//清空缓存

  int getSize();//获取缓存的个数

  ReadWriteLock getReadWriteLock();//获取读写锁

}

PerpetualCache实现类

public class PerpetualCache implements Cache {

  private final String id;

  private Map<Object, Object> cache = new HashMap<>();

  public PerpetualCache(String id) {
    this.id = id;
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public int getSize() {
    return cache.size();
  }

  @Override
  public void putObject(Object key, Object value) {
    cache.put(key, value);
  }

  @Override
  public Object getObject(Object key) {
    return cache.get(key);
  }

  @Override
  public Object removeObject(Object key) {
    return cache.remove(key);
  }

  @Override
  public void clear() {
    cache.clear();
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  @Override
  public boolean equals(Object o) {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    if (this == o) {
      return true;
    }
    if (!(o instanceof Cache)) {
      return false;
    }

    Cache otherCache = (Cache) o;
    return getId().equals(otherCache.getId());
  }

  @Override
  public int hashCode() {
    if (getId() == null) {
      throw new CacheException("Cache instances require an ID.");
    }
    return getId().hashCode();
  }

}

BlockingCache-----装饰器类

public class BlockingCache implements Cache {

  //阻塞的超时时长
  private long timeout;
  //被装饰的底层对象,一般是PerpetualCache
  private final Cache delegate;
  //锁对象集,粒度到key值
  private final ConcurrentHashMap<Object, ReentrantLock> locks;

  public BlockingCache(Cache delegate) {
    this.delegate = delegate;
    this.locks = new ConcurrentHashMap<>();
  }

  @Override
  public String getId() {
    return delegate.getId();
  }

  @Override
  public int getSize() {
    return delegate.getSize();
  }

  @Override
  public void putObject(Object key, Object value) {
    try {
      delegate.putObject(key, value);
    } finally {
      releaseLock(key);
    }
  }

  @Override
  public Object getObject(Object key) {
    acquireLock(key);//根据key获得锁对象,获取锁成功加锁,获取锁失败阻塞一段时间重试
    Object value = delegate.getObject(key);
    if (value != null) {//获取数据成功的,要释放锁
      releaseLock(key);
    }
    return value;
  }

  @Override
  public Object removeObject(Object key) {
    // despite of its name, this method is called only to release locks
    releaseLock(key);
    return null;
  }

  @Override
  public void clear() {
    delegate.clear();
  }

  @Override
  public ReadWriteLock getReadWriteLock() {
    return null;
  }

  private ReentrantLock getLockForKey(Object key) {
    ReentrantLock lock = new ReentrantLock();//创建锁
    ReentrantLock previous = locks.putIfAbsent(key, lock);//把新锁添加到locks集合中,如果添加成功使用新锁,如果添加失败则使用locks集合中的锁
    return previous == null ? lock : previous;
  }

//根据key获得锁对象,获取锁成功加锁,获取锁失败阻塞一段时间重试
  private void acquireLock(Object key) {
    //获得锁对象
    Lock lock = getLockForKey(key);
    if (timeout > 0) {//使用带超时时间的锁
      try {
        boolean acquired = lock.tryLock(timeout, TimeUnit.MILLISECONDS);
        if (!acquired) {//如果超时抛出异常
          throw new CacheException("Couldn‘t get a lock in " + timeout + " for the key " +  key + " at the cache " + delegate.getId());
        }
      } catch (InterruptedException e) {
        throw new CacheException("Got interrupted while trying to acquire lock for key " + key, e);
      }
    } else {//使用不带超时时间的锁
      lock.lock();
    }
  }

  private void releaseLock(Object key) {
    ReentrantLock lock = locks.get(key);
    if (lock.isHeldByCurrentThread()) {
      lock.unlock();
    }
  }

  public long getTimeout() {
    return timeout;
  }

  public void setTimeout(long timeout) {
    this.timeout = timeout;
  }
}

锁粒度的问题 粗粒度锁

当缓存中暂时没有数据时,如果不加锁,那么将会有多个线程同时访问数据库,通过加锁的操作就可以保证只有一个线程请求数据库,降低压力,不过锁粒度太大。

锁粒度的问题 细粒度锁(按key)

缓存装饰器解读

1、FifoCache

2、LoggingCache

3、ScheduledCache

4、BlockingCache

CacheKey解读

Mybatis中涉及到动态SQL的原因,缓存项的key不能仅仅通过一个String来表示,所以通过CacheKey来封装缓存的 Key值,CacheKey可以封装多个影响缓存项的因素;判断两个CacheKey是否相同关键是比较两个对象的hash值是 否一致;

构成CacheKey的对象

1、mappedStatment的id-----包括Mapper路径、名称是否相同,以及Mapper中id是否一致

2、指定查询结果集的范围(分页信息)

3、查询所使用的SQL语句

4、用户传递给SQL语句的实际参数值

源码分析

public class CacheKey implements Cloneable, Serializable {

  private static final long serialVersionUID = 1146682552656046210L;

  public static final CacheKey NULL_CACHE_KEY = new NullCacheKey();

  private static final int DEFAULT_MULTIPLYER = 37;
  private static final int DEFAULT_HASHCODE = 17;

  private final int multiplier;//参与hash计算的乘数
  private int hashcode;//CacheKey的hash值,在update函数中实时运算出来的
  private long checksum;//校验和,hash值的和
  private int count;//updateList的中元素个数
  // 8/21/2017 - Sonarlint flags this as needing to be marked transient.  While true if content is not serializable, this is not always true and thus should not be marked transient.
  //该集合中的元素觉得两个CacheKey是否相等
  private List<Object> updateList;

  public CacheKey() {
    this.hashcode = DEFAULT_HASHCODE;
    this.multiplier = DEFAULT_MULTIPLYER;
    this.count = 0;
    this.updateList = new ArrayList<>();
  }

  public CacheKey(Object[] objects) {
    this();
    updateAll(objects);
  }

  public void update(Object object) {
    //获取object的hash值
    int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
    //更新count、checksum以及hashcode的值
    count++;
    checksum += baseHashCode;
    baseHashCode *= count;
    hashcode = multiplier * hashcode + baseHashCode;
    //将对象添加到updateList中
    updateList.add(object);
  }

  public void updateAll(Object[] objects) {
    for (Object o : objects) {
      update(o);
    }
  }

  @Override
  public boolean equals(Object object) {
    if (this == object) {//比较是不是同一个对象
      return true;
    }
    if (!(object instanceof CacheKey)) {//是否类型相同
      return false;
    }

    final CacheKey cacheKey = (CacheKey) object;

    if (hashcode != cacheKey.hashcode) {//hashcode是否相同
      return false;
    }
    if (checksum != cacheKey.checksum) {//checksum是否相同
      return false;
    }
    if (count != cacheKey.count) {//count是否相同
      return false;
    }

    //以上都不相同,才按顺序比较updateList中元素的hash值是否一致
    for (int i = 0; i < updateList.size(); i++) {
      Object thisObject = updateList.get(i);
      Object thatObject = cacheKey.updateList.get(i);
      if (!ArrayUtil.equals(thisObject, thatObject)) {
        return false;
      }
    }
    return true;
  }

  @Override
  public int hashCode() {
    return hashcode;
  }
}

orm框架查询数据过程

反射的核心类

MetaObject:封装了对象元信息,包装了mybatis中五个核心的反射类。也是提供给外部使用的反射工具类, 可以利用它可以读取或者修改对象的属性信息;

ObjectFactory:MyBatis每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构建POJO;

ReflectorFactory:创建Reflector的工厂类,Reflector是mybatis反射模块的基础,每个Reflector对象都对应 一个类,在其中缓存了反射操作所需要的类元信息;

ObjectWrapper:对对象的包装,抽象了对象的属性信息,他定义了一系列查询对象属性信息的方法,以及更 新属性的方法;

ObjectWrapperFactory: ObjectWrapper 的工程类,用于创建ObjectWrapper ;

源码分析

A、ObjectFactory-----创建对象的工厂

1、ObjectFactory

public interface ObjectFactory {

  void setProperties(Properties properties);

  <T> T create(Class<T> type);

  <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

  <T> boolean isCollection(Class<T> type);

}

2、DefaultObjectFactory

public class DefaultObjectFactory implements ObjectFactory, Serializable {

  private static final long serialVersionUID = -8855120656740914948L;

  @Override
  public <T> T create(Class<T> type) {
    return create(type, null, null);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    //判断类是不是集合类,如果是集合类指定具体的实现类
    Class<?> classToCreate = resolveInterface(type);
    // we know types are assignable
    return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
  }

  @Override
  public void setProperties(Properties properties) {
    // no props for default
  }

  private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      //通过无参构造函数创建对象
      if (constructorArgTypes == null || constructorArgs == null) {
        constructor = type.getDeclaredConstructor();     //如果不是public修饰的,转为public修饰
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        return constructor.newInstance();
      }
      //根据指定的参数列表查找构造函数,并实例化对象
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (Exception e) {
      StringBuilder argTypes = new StringBuilder();
      if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
        argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null && !constructorArgs.isEmpty()) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
        argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

  protected Class<?> resolveInterface(Class<?> type) {
    Class<?> classToCreate;
    if (type == List.class || type == Collection.class || type == Iterable.class) {
      classToCreate = ArrayList.class;
    } else if (type == Map.class) {
      classToCreate = HashMap.class;
    } else if (type == SortedSet.class) { // issue #510 Collections Support
      classToCreate = TreeSet.class;
    } else if (type == Set.class) {
      classToCreate = HashSet.class;
    } else {
      classToCreate = type;
    }
    return classToCreate;
  }

  @Override
  public <T> boolean isCollection(Class<T> type) {
    return Collection.class.isAssignableFrom(type);
  }

}

B、ReflectorFactory-----对类的封装,获取构造器等保存起来

1、ReflectorFactory

public interface ReflectorFactory {

  boolean isClassCacheEnabled();

  void setClassCacheEnabled(boolean classCacheEnabled);

  Reflector findForClass(Class<?> type);
}

2、DefaultReflectorFactory

public class DefaultReflectorFactory implements ReflectorFactory {
  private boolean classCacheEnabled = true;
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();

  public DefaultReflectorFactory() {
  }

  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }

  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
      return reflectorMap.computeIfAbsent(type, Reflector::new);
    } else {
      return new Reflector(type);
    }
  }

}

3、Reflector-----提前将class、set、get、构造器等信息提前获取到,保存起来,方便后面使用

public class Reflector {

  private final Class<?> type;//对应的class
  private final String[] readablePropertyNames;//可读属性的名称集合,存在get方法即可读
  private final String[] writeablePropertyNames;//可写属性的名称集合,存在set方法即可写
  private final Map<String, Invoker> setMethods = new HashMap<>();//保存属性相关的set方法
  private final Map<String, Invoker> getMethods = new HashMap<>();//保存属性相关的get方法
  private final Map<String, Class<?>> setTypes = new HashMap<>();//保存属性相关的set方法入参类型
  private final Map<String, Class<?>> getTypes = new HashMap<>();//保存属性相关的get方法返回类型
  private Constructor<?> defaultConstructor;//class默认的构造函数

  //记录所有属性的名称集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

  public Reflector(Class<?> clazz) {
    type = clazz;
    addDefaultConstructor(clazz);//获取clazz的默认构造函数
    addGetMethods(clazz);//处理clazz中的get方法信息,填充getMethods、getTypes
    addSetMethods(clazz);//处理clazz中的set方法信息,填充setMethods、setTypes
    addFields(clazz);//处理没有get、set方法的属性
    //根据get、set方法初始化可读属性集合和可写属性集合
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    //初始化caseInsensitivePropertyMap
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }
}

C、ObjectWrapper-----对对象的封装,可以给对象赋值

1、ObjectWrapper

public interface ObjectWrapper {

  //获取对象指定属性的值
  Object get(PropertyTokenizer prop);

  //设置对象指定属性的值
  void set(PropertyTokenizer prop, Object value);

  String findProperty(String name, boolean useCamelCaseMapping);

  String[] getGetterNames();

  String[] getSetterNames();

  Class<?> getSetterType(String name);

  Class<?> getGetterType(String name);

  boolean hasSetter(String name);

  boolean hasGetter(String name);

  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
  //判断当前对象是否为集合
  boolean isCollection();

  //当前集合添加一个元素
  void add(Object element);

  //当前集合添加另外一个集合
  <E> void addAll(List<E> element);

}

2、ObjectWrapperFactory

public interface ObjectWrapperFactory {

  boolean hasWrapperFor(Object object);

  ObjectWrapper getWrapperFor(MetaObject metaObject, Object object);

}

下面通过一个junit测试将上面的类串起来:

//----------------源码分析之反射工具类的实例---------------------
    @Test
    public void reflectionTest(){
        //反射工具类初始化
        ObjectFactory objectFactory = new DefaultObjectFactory();
        //通过ObjectFactory创建TUser对象
        TUser user = objectFactory.create(TUser.class);
        ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
        ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
        MetaObject metaObject = MetaObject.forObject(user, objectFactory, objectWrapperFactory, reflectorFactory);

        //模拟数据库行数据转化成对象
        //1.模拟从数据库读取数据
        Map<String, Object> dbResult = new HashMap<>();
        dbResult.put("id", 1);
        dbResult.put("user_name", "lison");
        dbResult.put("real_name", "李晓宇");
        TPosition tp = new TPosition();
        tp.setId(1);
        dbResult.put("position_id", tp);
        //2.模拟映射关系
        Map<String, String> mapper = new HashMap<String, String>();
        mapper.put("id", "id");
        mapper.put("userName", "user_name");
        mapper.put("realName", "real_name");
        mapper.put("position", "position_id");

        //3.使用反射工具类将行数据转换成pojo
        BeanWrapper objectWrapper = (BeanWrapper) metaObject.getObjectWrapper();

        Set<Entry<String, String>> entrySet = mapper.entrySet();
        for (Entry<String, String> colInfo : entrySet) {
            String propName = colInfo.getKey();
            Object propValue = dbResult.get(colInfo.getValue());
            PropertyTokenizer proTokenizer = new PropertyTokenizer(propName);
            objectWrapper.set(proTokenizer, propValue);
        }
        System.out.println(metaObject.getOriginalObject());

}

原文地址:https://www.cnblogs.com/alimayun/p/12300678.html

时间: 2024-10-11 04:49:40

mybatis专题(三)-----mybatis源码学习的相关文章

Mybatis源码学习记录

一.对源码先上一个结构图: 源代码主要在org.apache.ibatis目录下,18个包,其中在应用中主要的包有:builder.session.cache.type.transaction.datasource.jdbc.mapping,提供支撑服务的包有annotation.binding.io.logging.plugin.reflection.scripting.exception.executor.parsing 二.从使用入手 MyBatis使用的三板斧是SqlSessionFac

struts2源码学习之初始化(三)

在上一篇struts2源码学习之初始化(二)中已经详细介绍了Dispatcher的初始化工作,只差最后一点,容器的创建.这一篇就仔细介绍容器的创建过程,初始化过程以及容器的作用.还是先从源码入手,上一篇已经分析到了Dispatcher类的init()的这段代码: Container container = init_PreloadConfiguration(); container.inject(this); init_CheckWebLogicWorkaround(container); 接着

EasyUI学习总结(三)——easyloader源码分析

EasyUI学习总结(三)--easyloader源码分析 easyloader模块是用来加载jquery easyui的js和css文件的,而且它可以分析模块的依赖关系,先加载依赖项.模块加载好了会调用parse模块来解析页面.把class是easyui开头的标签都转化成easyui的控件. 先看Demo1例子,再分析源代码. 1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>easyloader范例</tit

Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习

package cc.aa; import android.os.Environment; import android.view.MotionEvent; import android.view.View; public class UnderstandDispatchTouchEvent { /** * dispatchTouchEvent()源码学习及其注释 * 常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouchEvent->onTouchE

Java 源码学习系列(三)——Integer

Integer 类在对象中包装了一个基本类型 int 的值.Integer 类型的对象包含一个 int 类型的字段. 此外,该类提供了多个方法,能在 int 类型和 String 类型之间互相转换,还提供了处理 int 类型时非常有用的其他一些常量和方法. 类定义 public final class Integer extends Number implements Comparable<Integer> 从类定义中我们可以知道以下几点: 1.Integer类不能被继承 2.Integer类

Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分期.美团点评等都在1.2--面的时候被问过无数次,都问吐了&_&,其他公司笔试的时候,但凡有Java的题,都有集合相关考点,尤其hash表--现在总结下. Java集合概述 HashMap介绍 HashMap源码学习 关于HashMap的几个经典问题 HashTable介绍和源码学习 Hash

【iScroll源码学习02】分解iScroll三个核心事件点

前言 最近两天看到很多的总结性发言,我想想今年好像我的变化挺大的,是不是该晚上来水一发呢?嗯,决定了,晚上来水一发! 上周六,我们简单模拟了下iScroll的实现,周日我们开始了学习iScroll的源码,今天我们接着上次的记录学习,因为最近事情稍微有点多了 学习进度可能要放慢,而且iScroll这个库实际意义很大,不能囫囵吞枣的学习,要学到精华,并且要用于项目中的,所以初步规划是最近两周主要围绕iScroll展开 而后两个选择:① 分离iScroll代码用于项目:② 抄袭iScroll精华部分用

Hadoop源码学习笔记(1) ——第二季开始——找到Main函数及读一读Configure类

Hadoop源码学习笔记(1) ——找到Main函数及读一读Configure类 前面在第一季中,我们简单地研究了下Hadoop是什么,怎么用.在这开源的大牛作品的诱惑下,接下来我们要研究一下它是如何实现的. 提前申明,本人是一直搞.net的,对java略为生疏,所以在学习该作品时,会时不时插入对java的学习,到时也会摆一些上来,包括一下设计模式之类的.欢迎高手指正. 整个学习过程,我们主要通过eclipse来学习,之前已经讲过如何在eclipse中搭建调试环境,这里就不多述了. 在之前源码初

HSQLDB源码学习——数据库安装启动及JDBC连接

HSQLDB 是一个轻量级的纯Java开发的开放源代码的关系数据库系统.因为HSQLDB的轻量(占用空间小),使用简单,支持内存运行方式等特点,HSQLDB被广泛用于开发环境和某些中小型系统中. 在http://sourceforge.net/projects/hsqldb/files/下载了HSQLDB 1.8.0版本.把下载的zip文件解压缩至任意目录例如c:\hsqldb1.8便完成安装. hsqldb有四种运行模式: 一.内存(Memory-Only)模式:所有数据都在内存里操作.应用程