第六章、Mybatis的运行和解析
6.1 学习之前先了解:
- Mapper接口为什么能够运行? mybatis为我们创建了代理类
- 什么是代理模式?设计模式之代理模式
- 为什么要使用代理模式?通过代理来控制真正服务对象的访问,提供额外的服务。通过代理重写一些类来满足特定的需求
- 框架的灵魂之——反射:使用反射使得程序的配置性提高,程序更加灵活,降低模块之间的耦合度
- JDK动态代理——java.lang.reflect包提供支持(需要提供接口)
- 开源框架CGLIB动态代理
6.2 构建SqlSessionFactory(builder模式)
6.2.1构建Configuration
- Configuration的作用:所有的配置都会被读入这里并保存为一个单例
- 读入配置文件,包括全局的基础xml配置文件和xml映射器文件
- 初始化基础配置,比如mybatis的别名,一些重要的类对象,例如插件,映射器,ObjectFactory,TypeHandler对象
- 提供单例,为后续创建sessionFactory服务并提供配置的参数
- 执行一些重要的方法,初始化配置信息
2.mybatis读出XML配置信息,Configuration类做如下初始化
- properties全局参数
- settings设置
- typeAlias别名
- typeHandler 类型处理器
- ObjectFactory对象工厂
- plugin插件
- environment环境配置(数据源,事物)
- DatabaseIdprovider
- Mapper映射器
6.2.2 映射器的内部组成
以下类图只包含主要的属性和方法
- MappedStatement,保存映射器的一个节点(select|insert|update|delete)。包括我们配置的SQL,SQL的id,缓存信息,resultMap,parameterType,resultType,languageDriver等
- SqlS的一ource:提供SqlBound对象的地方,是MappedStatement个属性,根据参数和其他规则组装SQL
- SqlBound,简历SQL和参数的地方。三个常用属性:SQL,parameterObject,parameterMappings
6.2.3构建SQLSessionFactory:mybatis会根据configuration的配置所配置的信息来构建SQLSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
6.3 SQLSession的运行过程
6.3.1映射器的动态代理
- Mapper映射是通过动态代理实现的
- MapperProxyFactory——代理工厂,作用就是生成动态代理对象,绑定接口 mapperInterface
package org.apache.ibatis.binding;
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return this.mapperInterface;
}
public Map<Method, MapperMethod> getMethodCache() {
return this.methodCache;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
}
3. 真正的代理方法在在MapperProxy中定义,它是一个调用处理器InvocationHandler
public class MapperProxy<T> implements InvocationHandler, Serializable {
...
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
/**我们知道这个MapperProxy就是一个InvocationHandler(他的作用是jdk创建动态代理时用的,也就是我们会根据当前的接口创建一个这个接口的动态代理对象,使用动态代理对象再利用反射调用实际对象的目标方法。
然而动态代理对象里面的方法都是Interface规定的。但是动态代理对象也能调用比如toString(),hashCode()等这些方法呀,这些方法是所有类从Object继承过来的。
所以这个判断的根本作用就是,如果利用动态代理对象调用的是toString,hashCode,getClass等这些从Object类继承过来的方法,就直接反射调用。如果调用的是接口规定的方法。我们就用MapperMethod来执行。
结论:
1)、method.getDeclaringClass用来判断当前这个方法是哪个类的方法。
2)、接口创建出的代理对象不仅有实现接口的方法,也有从Object继承过来的方法
3)、实现的接口的方法method.getDeclaringClass是接口类型,比如com.atguigu.dao.EmpDao
从Object类继承过来的方法类型是java.lang.Object类型
4)、如果是Object类继承来的方法,直接反射调用
如果是实现的接口规定的方法,利用Mybatis的MapperMethod调用
*/
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
if (this.isDefaultMethod(method)) {
return this.invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
MapperMethod mapperMethod = this.cachedMapperMethod(method);
return mapperMethod.execute(this.sqlSession, args);
}
}
4. MapperMethod采用命令模式运行,根据上下文跳转,可能跳转到很多方法中。设计模式之——命令模式
为什么mybatis只使用Mapper接口就可以运行SQL?
因为映射器的namespace命名空间对应的就是接口的全路径,根据全路径和方法名便能够绑定起来,通过动态代理技术让接口跑起来。
6.3.2 SqlSession下的四大对象
映射器其实就是一个动态代理对象,进入到了MapperMethod的execute()方法,它经过简单的判断进入到SQLSession的增,删,改,查等方法,那么这些方法如何执行?
通过类名和方法名称就可以匹配到我们配置的SQL。我们不需要关注细节,只需要关注框架设计。
Mapper执行的过程是Executor ,StatementHandler,ParameterHandler,ResultSetHandler来完成数据库操作和返回结果的。
Executor:执行器,他来调度StatementHandler,ParameterHandler,ResultSetHandler执行相应的Sql
StatementHandler:它的作用是使用数据库的statement(PreparedStatement)执行操作,四大对象的核心,起到承上启下的作用
Parameterhandler:用于对SQL参数的处理
ResultHandler:是进行最后数据集(resultSet)封装处理
- 执行器Executor:真正执行JAVA和数据库交互的东西
有三种执行器,可以在mybatis配置文件的settings中进行配置
- SIMPLE:简易执行器,默认
- REUSE:是一种执行器重用预处理语句
- BATCH:执行器重用语句和批量更新,针对批量专用的执行器
他们都提供了查询和更新的方法,以及相关的事物方法。
- 执行器生成:在调用SqlSessionFactory获取sqlsession时配置类Configuration根据配置中的ExecutorType创建executor
//Configuration类中
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? this.defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (this.cacheEnabled) {
executor = new CachingExecutor((Executor)executor);
}
Executor executor = (Executor)this.interceptorChain.pluginAll(executor);
return executor;
}
- 以SimpleExecutor的查询方法为例 doQuery:Executor调度StatementHandler,ParameterHandler,ResultHandler执行相应的Sql
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
// mybatis根据Configuration构建StatementHandler
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
//resultHandler组装查询结果返回给调用者
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
//prepareStatement对SQl编译并且对参数进行初始化
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Connection connection = this.getConnection(statementLog);
//进行预编译和基础设置
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
// 设置参数,在parameterize方法中启用ParameterHandler处理参数
handler.parameterize(stmt);
return stmt;
}
- 数据库会话器StatementHandler——专门处理数据库会话的
mybatis如何创建StatementHAndler?
//Configuration类
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
//创建的真实对象是RoutingStatementHandler,它实现了StatementHandler接口
//StatementHandler也分为三种:SimpleStatement,PreparedStatement,CalleableStatement
//RoutingStatementHandler不是真正的服务对象它是通过适配器模式找到真正的StatementHandler来执行
//的,在初始化RoutingStatementHandler的时候他会根据上下文环境决定使用哪个StatementHandler
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
StatementHandler statementHandler = (StatementHandler)this.interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public class RoutingStatementHandler implements StatementHandler {
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch(ms.getStatementType()) {
case STATEMENT:
this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
- RoutingStatementHandler不是真正的服务对象它是通过适配器模式(对象适配器模式)找到真正的StatementHandler来执行的,StatementHandler也分为三种:SimpleStatement,PreparedStatement,CalleableStatement他们所对应的就是Executor的三种执行器
- 参数处理器Parameterhandler
从parameterObject中取出参数,使用typeHandler进行参数处理
- 结果集处理器ResultSetHandler:底层实现使用了JAVASSIST和CGLIB作为延迟加载,然后通过typeHandler和ObjectFactory进行组装结果再返回
总结:
1、获取sqlSessionFactory对象:
根据配置文件(全局,sql映射)初始化出Configuration对象
解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;
注意:MappedStatement:代表一个增删改查的详细信息
2、获取sqlSession对象
返回一个DefaultSQlSession对象,包含Executor和Configuration;
这一步会创建Executor对象;
Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)
3、获取接口的代理对象(MapperProxy)
DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;
使用MapperProxyFactory创建一个MapperProxy的代理对象
代理对象里面包含了,DefaultSqlSession(Executor)
4、执行增删改查方法
1)调用DefaultSqlSession的增删改查(Executor);
2)会创建一个StatementHandler对象。
(同时也会创建出ParameterHandler和ResultSetHandler)
3)调用StatementHandler预编译参数以及设置参数值;
使用ParameterHandler来给sql设置参数
4)调用StatementHandler的增删改查方法;
5)ResultSetHandler封装结果