Mybatis(二)——Mybatis的运行和解析

第六章、Mybatis的运行和解析

6.1 学习之前先了解:

  1.  Mapper接口为什么能够运行? mybatis为我们创建了代理类
  2. 什么是代理模式?设计模式之代理模式
  3. 为什么要使用代理模式?通过代理来控制真正服务对象的访问,提供额外的服务。通过代理重写一些类来满足特定的需求
  4. 框架的灵魂之——反射:使用反射使得程序的配置性提高,程序更加灵活,降低模块之间的耦合度
  5. JDK动态代理——java.lang.reflect包提供支持(需要提供接口)
  6. 开源框架CGLIB动态代理

6.2 构建SqlSessionFactory(builder模式)

6.2.1构建Configuration

  1. 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映射器的动态代理

  1. Mapper映射是通过动态代理实现的
  2. 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中进行配置

  1. SIMPLE:简易执行器,默认
  2. REUSE:是一种执行器重用预处理语句
  3. 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封装结果

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值