1. mybatis的骨架
1. mybatis的骨架
MyBatis整体架构

面向sqlsession编程
为什么分层: 提供可读性, 方便团队开发, 提高系统的伸缩性
门面模式: 为了遵循迪米特法则
springmvc的dispatchservelt也是一个门面模式
面向接口编程好处: 实现变化,我们使用的客户端不需要变化, 实现和使用解耦
开闭原则: 需求变化, 通过新增实现, 不要修改代码
迪米特法则: 尽量降低类之间的耦合
1. 日志模块
-
MyBatis没有提供日志的实现类,需要接入第三方的日志组件,但第三方日志组件都有各自的Log级别,且各不相同,而MyBatis统一提供了trace、debug、warn、error四个级别;
-
自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog;
-
日志的使用要优雅的嵌入到主体功能中;
适配器模式

日志模块的适配器模式类图

LogFactory
//自动扫描日志实现,并且第三方日志插件加载优先级如下:slf4J → commonsLoging → Log4J2 → Log4J → JdkLog
static {tryImplementation(LogFactory::useSlf4jLogging);tryImplementation(LogFactory::useCommonsLogging);tryImplementation(LogFactory::useLog4J2Logging);tryImplementation(LogFactory::useLog4JLogging);tryImplementation(LogFactory::useJdkLogging);tryImplementation(LogFactory::useNoLogging);
}
代理模式
给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用
- 屏蔽不必要的复杂性, 2. 给目标代码增强
静态代理会导致代理类的膨胀
Proxy(生成代理类), InvocationHandler(业务增强)
日志模块模式类图

BaseJdbcLogger:
//所有日志增强的抽象基类
public abstract class BaseJdbcLogger {//保存preparestatment中常用的set方法(占位符赋值)protected static final Set<String> SET_METHODS = new HashSet<>();//保存preparestatment中常用的执行sql语句的方法protected static final Set<String> EXECUTE_METHODS = new HashSet<>();// 为什么要设计成 map + list + list 方式, map方便使用hash算法, list肯定是为了方便使用contains方法或者遍历key,value//保存preparestatment中set方法的键值对private final Map<Object, Object> columnMap = new HashMap<>();//保存preparestatment中set方法的key值private final List<Object> columnNames = new ArrayList<>();//保存preparestatment中set方法的value值private final List<Object> columnValues = new ArrayList<>();protected Log statementLog;protected int queryStack;
ConnectionLogger:负责打印连接信息和SQL语句,并创建PreparedStatementLogger;
PreparedStatementLogger:负责打印参数信息,并创建ResultSetLogger
ResultSetLogger:负责打印数据结果信息;
org.apache.ibatis.logging.jdbc.ConnectionLogger
org.apache.ibatis.logging.jdbc.PreparedStatementLogger#invoke
源码调式(最好的记忆方式就是debug源码, 没有之一, 其他方式容易忘记, debug随时可以复习)
日志打印其实就是围绕Connection,PreparedStatement,ResultSet类的一系列注册在BaseJdbcLogger的EXECUTE_METHODS和SET_METHODS中的方法做日志逻辑增强,
主要是set*方法和execute,executeUpdate,executeQuery,addBatch方法
所以熟悉jdbc依然很重要, 框架也是围绕jdbc来做的
经典的jdbc代码
//STEP 1. 导入sql相关的包
@Test
public void QueryPreparedStatementDemo() {Connection conn = null;PreparedStatement stmt = null;List<TUser> users = new ArrayList<>();try {// STEP 2: 注册mysql的驱动Class.forName("com.mysql.jdbc.Driver");// STEP 3: 获得一个连接System.out.println("Connecting to database...");conn = DriverManager.getConnection(DB_URL, USER, PASS); // 被mybatis代理的类Connection->ConnectionLogger// STEP 4: 创建一个查询System.out.println("Creating statement...");String sql;sql = "SELECT * FROM t_user where userName= ? ";stmt = conn.prepareStatement(sql); // 被mybatis代理的类PreparedStatement->PreparedStatementLoggerstmt.setString(1, "lison");System.out.println(stmt.toString());//打印sqlResultSet rs = stmt.executeQuery(); // 被mybatis代理的类ResultSet->ResultSetLogger// ResultSet rs = stmt.getResultSet();// STEP 5: 从resultSet中获取数据并转化成beanwhile (rs.next()) {System.out.println("------------------------------");// Retrieve by column nameTUser user = new TUser();user.setId(rs.getInt("id"));user.setUserName(rs.getString("userName"));user.setRealName(rs.getString("realName"));user.setSex(rs.getByte("sex"));user.setMobile(rs.getString("mobile"));user.setEmail(rs.getString("email"));user.setNote(rs.getString("note"));System.out.println(user.toString());users.add(user);}// STEP 6: 关闭连接rs.close();stmt.close();conn.close();} catch (SQLException se) {// Handle errors for JDBCse.printStackTrace();} catch (Exception e) {// Handle errors for Class.forNamee.printStackTrace();} finally {// finally block used to close resourcestry {if (stmt != null)stmt.close();} catch (SQLException se2) {}// nothing we can dotry {if (conn != null)conn.close();} catch (SQLException se) {se.printStackTrace();}}System.out.println("-------------------------");System.out.println("there are "+users.size()+" users in the list!");
}
测试用例
private SqlSessionFactory sqlSessionFactory;@Before
public void init() throws IOException {//--------------------第一阶段---------------------------// 1.读取mybatis配置文件创SqlSessionFactoryString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);// 1.读取mybatis配置文件创SqlSessionFactorysqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);inputStream.close();
}@Test
// 快速入门
public void quickStart() throws IOException {//--------------------第二阶段---------------------------// 2.获取sqlSession SqlSession sqlSession = sqlSessionFactory.openSession();// 3.获取对应mapperTUserMapper mapper = sqlSession.getMapper(TUserMapper.class);//--------------------第三阶段---------------------------// 4.执行查询语句并返回单条数据TUser user = mapper.selectByPrimaryKey(2);System.out.println(user);System.out.println("----------------------------------");// 5.执行查询语句并返回多条数据List<TUser> users = mapper.selectAll();for (TUser tUser : users) {System.out.println(tUser);}
}
此方法打断点
org.apache.ibatis.executor.SimpleExecutor#prepareStatement

会发现入口方法是: org.apache.ibatis.executor.SimpleExecutor#doQuery

所以默认的执行器是SimpleExecutor, 我们所有mybatis的核心源码多数会从这里进入
接着看源码
protected Connection getConnection(Log statementLog) throws SQLException {Connection connection = transaction.getConnection(); // 拿到原生的connection, if (statementLog.isDebugEnabled()) { // 然后对齐生成具备logger能力的代理对象, jdk代理return ConnectionLogger.newInstance(connection, statementLog, queryStack);} else {return connection;}
}
所以我们一定要在org.apache.ibatis.logging.jdbc.ConnectionLogger#invoke打一个断点吧, 后面调试一定会停下来

public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {try {//如果是从Obeject继承的方法直接忽略if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, params);}//如果是调用prepareStatement、prepareCall、createStatement的方法,打印要执行的sql语句//并返回prepareStatement的代理对象,让prepareStatement也具备日志能力,打印参数if ("prepareStatement".equals(method.getName())) {if (isDebugEnabled()) {debug(" Preparing: " + removeBreakingWhitespace((String)params[0]), true);//打印sql语句}PreparedStatement stmt = (PreparedStatement)method.invoke(connection, params);stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);//创建代理对象return stmt;} else if ("prepareCall".equals(method.getName())) {if (isDebugEnabled()) {debug(" Preparing: " + removeBreakingWhitespace((String)params[0]), true);//打印sql语句}PreparedStatement stmt = (PreparedStatement)method.invoke(connection, params);//创建代理对象stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);return stmt;} else if ("createStatement".equals(method.getName())) {Statement stmt = (Statement)method.invoke(connection, params);stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);//创建代理对象return stmt;} else {return method.invoke(connection, params);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}
}
同样PreparedStatement或者Statement也被代理增强了, 接下来看PreparedStatementLogger

@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, params);}if (EXECUTE_METHODS.contains(method.getName())) {//增强PreparedStatement的execute相关方法if (isDebugEnabled()) {debug("Parameters: " + getParameterValueString(), true);//当方法执行时,通过动态代理打印参数}clearColumnInfo();if ("executeQuery".equals(method.getName())) {//返回动态代理能力的resultSetResultSet rs = (ResultSet)method.invoke(statement, params);return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);} else {return method.invoke(statement, params);}} else if (SET_METHODS.contains(method.getName())) {//将参数设置到columnMap、columnNames、columnValues,为打印参数做好准备// 设置方法比上面的打印日志的方法先打印if ("setNull".equals(method.getName())) {setColumn(params[0], null); // 往三个容器设置参数} else {setColumn(params[0], params[1]);}return method.invoke(statement, params);} else if ("getResultSet".equals(method.getName())) {//返回动态代理能力的resultSetResultSet rs = (ResultSet)method.invoke(statement, params);return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);} else if ("getUpdateCount".equals(method.getName())) {// 如果是更新,直接打印影响的行数int updateCount = (Integer)method.invoke(statement, params);if (updateCount != -1) {debug(" Updates: " + updateCount, false);}return updateCount;} else {return method.invoke(statement, params);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}
}
ResultSet也被代理增强, 看ResultSetLogger代码

public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, params);} Object o = method.invoke(rs, params);//执行result.next方法,判断是否还有数据if ("next".equals(method.getName())) {//如果还有数据,计数器rows加一if (((Boolean) o)) {rows++;if (isTraceEnabled()) {ResultSetMetaData rsmd = rs.getMetaData();final int columnCount = rsmd.getColumnCount();if (first) {first = false;printColumnHeaders(rsmd, columnCount);}printColumnValues(columnCount);}} else {debug(" Total: " + rows, false);//如果没有数据了,打印rows,打印查询出来的数据条数}}clearColumnInfo();return o;} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}
}
从源码可以看出来是一个非常典型的代理模式的应用, 而BaseJdbcLogger类中聚合Log就是一个很好的适配器模式的应用
适配器核心代码
public abstract class BaseJdbcLogger { // //保存preparestatment中常用的set方法(占位符赋值)protected static final Set<String> SET_METHODS = new HashSet<>();//保存preparestatment中常用的执行sql语句的方法protected static final Set<String> EXECUTE_METHODS = new HashSet<>();// 为什么要设计成 map + list + list 方式, map方便使用hash算法, list肯定是为了方便使用contains方法或者遍历key,value//保存preparestatment中set方法的键值对private final Map<Object, Object> columnMap = new HashMap<>();//保存preparestatment中set方法的key值private final List<Object> columnNames = new ArrayList<>();//保存preparestatment中set方法的value值private final List<Object> columnValues = new ArrayList<>();protected Log statementLog; // 被适配的接口统一的接口...
}
public final class LogFactory {/*** Marker to be used by logging implementations that support markers*/public static final String MARKER = "MYBATIS";//被选定的第三方日志组件适配器的构造方法private static Constructor<? extends Log> logConstructor; // 组合log接口...
}
public class Log4jImpl implements Log { // 适配成新产品的接口类: mybatis定义的接口类Logprivate static final String FQCN = Log4jImpl.class.getName();private final Logger log; // 原来产品的接口类: log4j的接口类 Logger, 被复用的代理...
}
总结: 使用了适配器模式和代理模式
2. 数据源模块分析
数据源模块重点数据源的创建和数据库连接池的源码分析;数据源创建比较复杂,对于复杂对象的创建,可以考虑使用工厂模式来优化,接下来介绍下简单工厂模式和工厂模式;
简单工厂模式和工厂模式略
1. 数据源的创建
数据源对象是比较复杂的对象,其创建过程相对比较复杂,对于 MyBatis 创建一个数据源, 具体来讲有如下难点:
-
常见的数据源组件都实现了 javax.sql.DataSource 接口;
-
MyBatis 不但要能集成第三方的数据源组件,自身也提供了数据源的实现;
-
一般情况下,数据源的初始化过程参数较多,比较复杂;
综上所述,数据源的创建是一个典型使用工厂模式的场景

- DataSource:数据源接口,JDBC 标准规范之一,定义了获取获取 Connection 的方法;
- UnPooledDataSource:不带连接池的数据源,获取连接的方式和手动通过 JDBC 获取连接的方式是一样的;
- PooledDataSource:带连接池的数据源,提高连接资源的复用性,避免频繁创建、关闭连接资源带来的开销;
- DataSourceFactory:工厂接口,定义了创建 Datasource 的方法;
- UnpooledDataSourceFactory:工厂接口的实现类之一,用于创建 UnpooledDataSource(不 带连接池的数据源);
- PooledDataSourceFactory:工厂接口的实现类之一,用于创建 PooledDataSource(带连 接池的数据源);
2. 数据库连接池技术解析
数据库连接池技术是提升数据库访问效率常用的手段,使用连接池可以提高连接资源的复用 性,避免频繁创建、关闭连接资源带来的开销。MyBatis 内 部就带了一个连接池的实现,接下来重点解析连接池技术的数据结构和算法;先重点分析下 跟连接池相关的关键类:
- PooledDataSource:一个简单,同步的、线程安全的数据库连接池
- PooledConnection:使用动态代理封装了真正的数据库连接对象,在连接使用之前和关闭时进行增强;
- PoolState:用于管理 PooledConnection 对象状态的组件,通过两个 list 分别管理空闲状态的连接资源和活跃状态的连接资源,如下图,需要注意的是这两个 List 使用 ArrayList实现,存在并发安全的问题,因此在使用时,注意加上同步控制;
重点解析获取资源和回收资源的流程,获取连接资源的过程如下图:
参考代码:org.apache.ibatis.datasource.pooled.PooledDataSource.popConnection(String, String)
**popConnection(): ** 获取连接资源

回收资源参考代码:
org.apache.ibatis.datasource.pooled.PooledDataSource.pushConnection(PooledConnection)
org.apache.ibatis.datasource.pooled.PooledConnection#isValid
检测状态和ping连接
public boolean isValid() {return valid && realConnection != null && dataSource.pingConnection(this);
}
3. 缓存模块分析
基于map实现的
除核心功能之外,有很多额外的附加功能,如:防止缓存击穿,添加缓存清空策略(fifo、lru)、序列化功能、日志能力、定时清空能力等;
怎么样优雅的为核心功能添加多种附加能力?使用动态代理或继承的办法扩展多种附加功能?
这些方式是静态的,用户不能控制增加行为的方式和时机。另外,新功能的存在多种组合,使用继承可能导致大量子类存在;
优化思路:装饰器模式是一种用于代替继承的技术,无需通过继承增加子类就能扩展对象的新功能。使用对象的关联关系代替继承关系,更加灵活,同时避免类型体系的快速膨胀;
装饰器模式类图:

mybatis 的缓存装饰模式类图

Cache:Cache接口是缓存模块的核心接口,定义了缓存的基本操作;
PerpetualCache:在缓存模块中扮演ConcreteComponent角色,使用HashMap来实现cache的相关操作;
BlockingCache:阻塞版本的缓存装饰器,保证只有一个线程到数据库去查找指定的key对应的数据;
缓存雪崩问题
- 当缓存失效, 竞争锁然后查询数据库(粗粒度锁和细粒度锁)
- 不同失效时间
BlockingCache通过ConcurrentHashMap实现细粒度锁
核心代码:
org.apache.ibatis.cache.decorators.BlockingCache#getLockForKey
private ReentrantLock getLockForKey(Object key) {ReentrantLock lock = new ReentrantLock();//创建锁ReentrantLock previous = locks.putIfAbsent(key, lock);//把新锁添加到locks集合中,如果添加成功使用新锁,如果添加失败则使用locks集合中的锁return previous == null ? lock : previous; // 如果为null使用新锁, 如果不为null, 使用老锁, 新锁会被jvm回收
}
当前线程才能解锁(和redis锁的防止锁误删一样思路)
private void releaseLock(Object key) {ReentrantLock lock = locks.get(key);if (lock.isHeldByCurrentThread()) {lock.unlock();}
}
为什么使用CacheKey作为缓存key, 不用String
MyBatis中涉及到动态SQL的原因,缓存项的key不能仅仅通过一个String来表示,所以通过CacheKey来封装缓存的Key值,CacheKey可以封装多个影响缓存项的因素;判断两个CacheKey是否相同关键是比较两个对象的hash值是否一致;
构成CacheKey的对象四个要素
-
mappedStatment的id
-
查询所使用的SQL语句
-
指定查询结果集的范围(分页信息)
-
用户传递给SQL语句的实际参数值
四个关键的属性
public class CacheKey implements Cloneable, Serializable {...private final int multiplier;//参与hash计算的乘数, 固定值37// 以下三个值为了提高比较的效率private int hashcode;//CacheKey的hash值,在update函数中实时运算出来的private long checksum;//校验和,hash值的和private int count;//updateList的中元素个数, 最终会有四个...public CacheKey() {this.hashcode = DEFAULT_HASHCODE;this.multiplier = DEFAULT_MULTIPLYER;this.count = 0;this.updateList = new ArrayList<>();}...
}
重点方法update和equals
org.apache.ibatis.cache.CacheKey#update
帮助计算hashCode,checkSum和count三个值的
// 每次变化一个要素就会执行一次update
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); // 最终会有四个值
}
org.apache.ibatis.cache.CacheKey#equals
@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;
}
二级缓存实现类: CachingExecutor
mapper.xml中通过cache开启二级缓存
<mapper namespace="..."><cache>cache>
mapper>
入口方法: query
org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {//获取sql语句信息,包括占位符,参数等信息BoundSql boundSql = ms.getBoundSql(parameterObject);//拼装缓存的key值CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);// 执行查询return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
执行查询: 二级缓存->一级缓存
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds,ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {//从MappedStatement中获取二级缓存Cache cache = ms.getCache(); // 不开启二级缓存, 这里将会直接返回null, 会调用一级缓存的query方法if (cache != null) {flushCacheIfRequired(ms);if (ms.isUseCache() && resultHandler == null) {ensureNoOutParams(ms, boundSql);@SuppressWarnings("unchecked") List<E> list = (List<E>)tcm.getObject(cache, key);//从二级缓存中获取数据if (list == null) {//二级缓存为空,才会调用BaseExecutor.querylist = delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);tcm.putObject(cache, key, list); // issue #578 and #116}return list;}}return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
MappedStatement
public final class MappedStatement {private String resource;//节点的完整的id属性,包括命名空间private Configuration configuration;private String id;//节点的id属性private Integer fetchSize;//节点的fetchSize属性,查询数据的条数private Integer timeout;//节点的timeout属性,超时时间private StatementType statementType;//节点的statementType属性,默认值:StatementType.PREPARED;private ResultSetType resultSetType;//节点的resultSetType属性,jdbc知识private SqlSource sqlSource;//节点中sql语句信息private Cache cache;//对应的二级缓存private ParameterMap parameterMap;//已废弃private List<ResultMap> resultMaps;//节点的resultMaps属性private boolean flushCacheRequired;//节点的flushCache属性是否刷新缓存private boolean useCache;//节点的useCache属性是否使用二级缓存private boolean resultOrdered;private SqlCommandType sqlCommandType;//sql语句的类型,包括:INSERT, UPDATE, DELETE, SELECTprivate KeyGenerator keyGenerator;//节点keyGenerator属性private String[] keyProperties;private String[] keyColumns;private boolean hasNestedResultMaps;//是否有嵌套resultMapprivate String databaseId;private Log statementLog;private LanguageDriver lang;private String[] resultSets;//多结果集使用
一级缓存类: BaseExecutor
一级缓存的核心实现类: PerpetualCache被BlockingCache装饰的类
public abstract class BaseExecutor implements Executor {private static final Log log = LogFactory.getLog(BaseExecutor.class);protected Transaction transaction;//事务对象protected Executor wrapper;//封装的Executor对象protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;//延迟加载的队列protected PerpetualCache localCache;//一级缓存的实现,PerpetualCacheprotected PerpetualCache localOutputParameterCache;//一级缓存用于缓存输出的结果protected Configuration configuration;//全局唯一configuration对象的引用protected int queryStack;//用于嵌套查询的的层数private boolean closed;
public class PerpetualCache implements Cache {private final String id;private Map<Object, Object> cache = new HashMap<>(); // hashmap
org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)throws SQLException {//获取sql语句信息,包括占位符,参数等信息BoundSql boundSql = ms.getBoundSql(parameter);//拼装缓存的key值CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
测试用例
记得要开启
<cache>cache>
// 快速入门
public void quickStart() throws IOException {//--------------------第二阶段---------------------------// 2.获取sqlSessionSqlSession sqlSession = sqlSessionFactory.openSession();// 3.获取对应mapper 被代理TUserMapper mapper = sqlSession.getMapper(TUserMapper.class);//--------------------第三阶段---------------------------// 4.执行查询语句并返回单条数据TUser user = mapper.selectByPrimaryKey(2);System.out.println(user);System.out.println("----------------------------------");// 5.执行查询语句并返回多条数据List<TUser> users = mapper.selectAll();for (TUser tUser : users) {System.out.println(tUser);}
}
二级缓存类打断点

被装饰的实例图

其中SynchronozedCache没有做任何逻辑补充, 只是对每个方法加了Synchronized关键字, 为了同步控制
其中典型的LoggingCache也是记录日志作用, 只有getObject加了日志
@Override
public Object getObject(Object key) {requests++;final Object value = delegate.getObject(key);if (value != null) {hits++;}if (log.isDebugEnabled()) {log.debug("Cache Hit Ratio [" + getId() + "]: " + getHitRatio());}return value;
}
4. 反射模块分析
1. orm框架查询数据过程

- 实例化对象是 ReflectorFactory和Reflector完成
- 包装对象是ObjectWrapperFactory和ObjectWrapper完成
- MetaObject封装了api的使用细节
2. Mybatis反射模块的核心类
-
ObjectFactory:MyBatis每次创建结果对象的新实例时,它都会使用对象工厂(ObjectFactory)去构建POJO;
-
ReflectorFactory:创建Reflector的工厂类,Reflector是MyBatis反射模块的基础,每个Reflector对象都对应一个类,在其中缓存了反射操作所需要的类元信息;
-
ObjectWrapperFactory: ObjectWrapper 的工厂类,用于创建ObjectWrapper ;
-
ObjectWrapper:对对象的包装,抽象了对象的属性信息,他定义了一系列查询对象属性信息的方法,以及更新属性的方法;
-
MetaObject:封装了对象元信息,包装了MyBatis中五个核心的反射类。也是提供给外部使用的反射工具类,可以利用它可以读取或者修改对象的属性信息;
3. Mybatis反射模块的核心类图

4. DefaultObjectFactory
创建对象
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {//判断类是不是集合类,如果是集合类指定具体的实现类Class<?> classToCreate = resolveInterface(type);// we know types are assignablereturn (T)instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}
5. Reflector

public class Reflector {// 1. 类private final Class<?> type;//对应的class// 2. get/set属性private final String[] readablePropertyNames;//可读属性的名称集合,存在get方法即可读private final String[] writeablePropertyNames;//可写属性的名称集合,存在set方法即可写// 3. get/set方法private final Map<String, Invoker> setMethods = new HashMap<>();//保存属性相关的set方法private final Map<String, Invoker> getMethods = new HashMap<>();//保存属性相关的get方法// 4. get返回值/set入参private final Map<String, Class<?>> setTypes = new HashMap<>();//保存属性相关的set方法入参类型private final Map<String, Class<?>> getTypes = new HashMap<>();//保存属性相关的get方法返回类型// 5. 构造器private Constructor<?> defaultConstructor;//class默认的构造函数// 6. 记录所有属性的名称集合private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();// 通过反射去创建get方法的MethodInvokerprivate void addGetMethod(String name, Method method) {if (isValidPropertyName(name)) {getMethods.put(name, new MethodInvoker(method));Type returnType = TypeParameterResolver.resolveReturnType(method, type);getTypes.put(name, typeToClass(returnType));}}private void addSetMethods(Class<?> cls) {Map<String, List<Method>> conflictingSetters = new HashMap<>();Method[] methods = getClassMethods(cls);for (Method method : methods) {String name = method.getName();if (name.startsWith("set") && name.length() > 3) {if (method.getParameterTypes().length == 1) {name = PropertyNamer.methodToProperty(name);addMethodConflict(conflictingSetters, name, method);}}}resolveSetterConflicts(conflictingSetters);}
6. DefaultReflectorFactory
package org.apache.ibatis.reflection;import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;// reflect的简单工厂类
public class DefaultReflectorFactory implements ReflectorFactory {private boolean classCacheEnabled = true;//用于缓存pojo的元数据,避免反射性能较低的问题private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();public DefaultReflectorFactory() {}@Overridepublic boolean isClassCacheEnabled() {return classCacheEnabled;}@Overridepublic void setClassCacheEnabled(boolean classCacheEnabled) {this.classCacheEnabled = classCacheEnabled;}@Overridepublic Reflector findForClass(Class<?> type) {if (classCacheEnabled) {// synchronized (type) removed see issue #461return reflectorMap.computeIfAbsent(type, Reflector::new);} else {return new Reflector(type);}}}
测试用例
@Test
public void reflectionTest() {//反射工具类初始化ObjectFactory objectFactory = new DefaultObjectFactory();TUser user = objectFactory.create(TUser.class);ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();ReflectorFactory reflectorFactory = new DefaultReflectorFactory();MetaObject metaObject = MetaObject.forObject(user, objectFactory, objectWrapperFactory, reflectorFactory);//使用Reflector读取类元信息Reflector findForClass = reflectorFactory.findForClass(TUser.class);Constructor<?> defaultConstructor = findForClass.getDefaultConstructor();String[] getablePropertyNames = findForClass.getGetablePropertyNames();String[] setablePropertyNames = findForClass.getSetablePropertyNames();System.out.println(defaultConstructor.getName());System.out.println(Arrays.toString(getablePropertyNames));System.out.println(Arrays.toString(setablePropertyNames));//使用ObjectWrapper读取对象信息,并对对象属性进行赋值操作TUser userTemp = new TUser();ObjectWrapper wrapperForUser = new BeanWrapper(metaObject, userTemp);String[] getterNames = wrapperForUser.getGetterNames();String[] setterNames = wrapperForUser.getSetterNames();System.out.println(Arrays.toString(getterNames));System.out.println(Arrays.toString(setterNames));PropertyTokenizer prop = new PropertyTokenizer("userName");wrapperForUser.set(prop, "lison");System.out.println(userTemp);
}
pojo没有按照规范生成getset方法, 同样会生成对应的方法
调试的核心代码入口
public Reflector(Class<?> clazz) {type = clazz;addDefaultConstructor(clazz);//获取clazz的默认构造函数addGetMethods(clazz);//处理clazz中的get方法信息,填充getMethods、getTypesaddSetMethods(clazz);//处理clazz中的set方法信息,填充setMethods、setTypesaddFields(clazz);//处理没有get、set方法的属性//根据get、set方法初始化可读属性集合和可写属性集合readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);//初始化caseInsensitivePropertyMapfor (String propName : readablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}for (String propName : writeablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}
}
生成set方法的核心类
public class SetFieldInvoker implements Invoker {private final Field field;public SetFieldInvoker(Field field) {this.field = field;}@Overridepublic Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {field.set(target, args[0]);return null;}@Overridepublic Class<?> getType() {return field.getType();}
}
7. BeanWrapper
用例给pojo赋值的对象
public class BeanWrapper extends BaseWrapper {private final Object object;//实例对象private final MetaClass metaClass;//类的元数据public BeanWrapper(MetaObject metaObject, Object object) {super(metaObject);this.object = object;this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());}@Overridepublic void set(PropertyTokenizer prop, Object value) {if (prop.getIndex() != null) {Object collection = resolveCollection(prop, object);setCollectionValue(prop, collection, value);} else {setBeanProperty(prop, object, value);}}
CustomObjectWrapperFactory
public class CustomObjectWrapperFactory implements org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory {@Overridepublic boolean hasWrapperFor(Object object) {return object.getClass().equals(CustomCollection.class);}@Overridepublic ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {return new org.apache.ibatis.submitted.custom_collection_handling.CustomObjectWrapper((CustomCollection)object);}}
8. MetaObject
封装了对象元信息,包装了MyBatis中五个核心的反射类。也是提供给外部使用的反射工具类,可以利用它可以读取或者修改对象的属性信息;
public class MetaObject {//原始的java对象private final Object originalObject;//对对象的包装,抽象了对象的属性信息,他定义了一系列查询对象属性信息的方法,以及更新属性的方法;private final ObjectWrapper objectWrapper;private final ObjectFactory objectFactory;private final ObjectWrapperFactory objectWrapperFactory;private final ReflectorFactory reflectorFactory;
使用例子
{//模拟数据库行数据转化成对象ObjectFactory objectFactory = new DefaultObjectFactory();TUser user = objectFactory.create(TUser.class);MetaObject metaObject = SystemMetaObject.forObject(user);//1.模拟从数据库读取数据Map<String, Object> dbResult = new HashMap<>();dbResult.put("id", 1);dbResult.put("userName", "lison");dbResult.put("realName", "李晓宇");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", "userName");mapper.put("realName", "realName");mapper.put("position", "position_id");//3.使用反射工具类将行数据转换成pojo//获取BeanWrapper,既包括类元数据,同时还能对对象的属性赋值BeanWrapper objectWrapper = (BeanWrapper)metaObject.getObjectWrapper();Set<Entry<String, String>> entrySet = mapper.entrySet();//遍历映射关系for (Entry<String, String> colInfo : entrySet) {String propName = colInfo.getKey();//获得pojo的字段名称Object propValue = dbResult.get(colInfo.getValue());//模拟从数据库中加载数据对应列的值PropertyTokenizer proTokenizer = new PropertyTokenizer(propName);objectWrapper.set(proTokenizer, propValue);//将数据库的值赋值到pojo的字段中}System.out.println(metaObject.getOriginalObject());
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
