mybatis源码解析

  • 2019 年 12 月 31 日
  • 筆記

文章目录

1. Configuration

2. MapperRegistry

3. MappedStatement

4. MapperProxyFactory

5. MapperProxy

6. MapperMethod

7. BoundSql

8. ResultHandler

9. TypeHandler

10. Mybatis四大关键类

10.1. Executor

10.2. ParameterHandler

10.3. ResultSetHandler

10.4. StatementHandler

11. 流程图

11.1. Mybatis创建MapperProxy

11.2. Mybaits执行查询

11.3. Mybaits执行查询的简化版

Configuration

  • mybatis的全局配置类,其中封装了mybatis的全部配置,在mybaits加载全局配置文件和Mapper文件的时候,会将里面的xml中的各个元素解析出来,封装在其中。
  • 主要的解析流程在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration有详细描述。
private void parseConfiguration(XNode root) {      try {        //issue #117 read properties first        propertiesElement(root.evalNode("properties"));        Properties settings = settingsAsProperties(root.evalNode("settings"));        loadCustomVfs(settings);        loadCustomLogImpl(settings);        typeAliasesElement(root.evalNode("typeAliases"));        pluginElement(root.evalNode("plugins"));        objectFactoryElement(root.evalNode("objectFactory"));        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));        reflectorFactoryElement(root.evalNode("reflectorFactory"));        settingsElement(settings);        // read it after objectFactory and objectWrapperFactory issue #631        environmentsElement(root.evalNode("environments"));        databaseIdProviderElement(root.evalNode("databaseIdProvider"));        typeHandlerElement(root.evalNode("typeHandlers"));        mapperElement(root.evalNode("mappers"));      } catch (Exception e) {        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);      }    }

MapperRegistry

  • 其中存放了所有的Mapper信息,相当于Mapper的注册中心
  • 所有的Mapper信息都存放在Map<Class<?>, MapperProxyFactory<?>> knownMappers这个Map中,key是Mapper的全类名,value是MapperProxyFactory对象(用来创建代理对象)
  • 在mybaits加载配置文件解析Mapper的时候,会调用addMapper方法,将其添加到Map中。在获取Mapper的时候会调用getMapper方法,利用MapperProxyFactory对象创建一个代理对象返回。

MappedStatement

  • 封装了一个增删改查标签的全部属性,一个标签就是一个MappedStatement,保存在全局配置类中的Map<String, MappedStatement> mappedStatements

MapperProxyFactory

  • 创建Mapper代理对象的工厂类,其中最重要的两个方法如下:
//使用MapperProxy创建一个代理对象  protected T newInstance(MapperProxy<T> mapperProxy) {      return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);    }      public T newInstance(SqlSession sqlSession) {        //封装一个MapperProxy      final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);      return newInstance(mapperProxy);    }

MapperProxy

  • Mapper的代理类,实现了InvocationHandler,当mapper调用方法的时候真正执行的方法是invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {      try {        if (Object.class.equals(method.getDeclaringClass())) {          return method.invoke(this, args);        } else if (method.isDefault()) {          return invokeDefaultMethod(proxy, method, args);        }      } catch (Throwable t) {        throw ExceptionUtil.unwrapThrowable(t);      }      final MapperMethod mapperMethod = cachedMapperMethod(method);      return mapperMethod.execute(sqlSession, args);    }
  • 其中的属性有sqlSession(其中的属性有全局配置类Configuration)、mapperInterface(mapper真正的接口)、Map<Method, MapperMethod> methodCache

MapperMethod

  • 封装了Mapper接口中的单个方法,其中有两个重要的属性,如下:
    • SqlCommand:封装方法的全类名和执行的增删改查的类型(SqlCommandType的枚举类型)
    • MethodSignature:方法的签名,其中封装了该方法的一些信息,比如返回类型,返回值的类型等信息。
  • 其中有一个public Object execute(SqlSession sqlSession, Object[] args)方法,用来执行方法,如下:
    • 根据方法的不同类型,执行不同的逻辑
public Object execute(SqlSession sqlSession, Object[] args) {      Object result;      switch (command.getType()) {        case INSERT: {          Object param = method.convertArgsToSqlCommandParam(args);          result = rowCountResult(sqlSession.insert(command.getName(), param));          break;        }        case UPDATE: {          Object param = method.convertArgsToSqlCommandParam(args);          result = rowCountResult(sqlSession.update(command.getName(), param));          break;        }        case DELETE: {          Object param = method.convertArgsToSqlCommandParam(args);          result = rowCountResult(sqlSession.delete(command.getName(), param));          break;        }        case SELECT:          if (method.returnsVoid() && method.hasResultHandler()) {            executeWithResultHandler(sqlSession, args);            result = null;          } else if (method.returnsMany()) {            result = executeForMany(sqlSession, args);          } else if (method.returnsMap()) {            result = executeForMap(sqlSession, args);          } else if (method.returnsCursor()) {            result = executeForCursor(sqlSession, args);          } else {            Object param = method.convertArgsToSqlCommandParam(args);            result = sqlSession.selectOne(command.getName(), param);            if (method.returnsOptional()                && (result == null || !method.getReturnType().equals(result.getClass()))) {              result = Optional.ofNullable(result);            }          }          break;        case FLUSH:          result = sqlSession.flushStatements();          break;        default:          throw new BindingException("Unknown execution method for: " + command.getName());      }      if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {        throw new BindingException("Mapper method '" + command.getName()            + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");      }      return result;    }

BoundSql

  • 封装了SQL语句的信息,包括需要执行的sql语句,参数列表等信息,通过MappedStatement中getBoundSql()方法创建。

ResultHandler

TypeHandler

Mybatis四大关键类

Executor

  • 执行器:在mybaits中负责执行增删改查和事务的操作,mybatis会根据不同的executorType创建不同的执行器
  • Mybaits的执行器主要有如下的类型:
    • ExecutorType.SIMPLE:这个执行器类型不做特殊的事情。它为每个语句的执行创建一个新的预处理语句。
    • ExecutorType.REUSE:这个执行器类型会复用预处理语句。
    • ExecutorType.BATCH:这个执行器会批量执行所有更新语句,如果 SELECT 在它们中间执行,必要时请把它们区分开来以保证行为的易读性。
  • 我们通过SqlSessionFactory创建sqlSession的时候可以传入执行器的类型,如果不传入,默认使用Simple类型的,在源码中的创建的执行的逻辑如下:
//org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)    public Executor newExecutor(Transaction transaction, ExecutorType executorType) {      //如果没有指定,使用默认的类型      executorType = executorType == null ? defaultExecutorType : executorType;      executorType = executorType == null ? ExecutorType.SIMPLE : executorType;      Executor 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 (cacheEnabled) {        executor = new CachingExecutor(executor);      }      executor = (Executor) interceptorChain.pluginAll(executor);      return executor;    }
  • 实现类型如下:
    • BatchExecutor:批量执行器
    • ReuseExecutor:复用执行器
    • SimpleExecutor:简单执行器,默认的
    • CachingExecutor:缓存执行器,mybaits中默认使用的执行器,用于二级缓存

ParameterHandler

  • 主要作用就是设置为Statement设置参数

ResultSetHandler

  • 主要作用就是对最后的结果进行处理

StatementHandler

  • 顾名思义就是创建JDBC中的Statement,实现类如下:
    • SimpleStatementHandler
    • PreparedStatementHandler
    • CallableStatementHandler
  • 主要的作用就是处理Statement,执行查询,对参数、查询结果进行处理。
  • 主要的方法如下:
    • Statement prepare(Connection connection, Integer transactionTimeout):创建Statement
    • void parameterize(Statement statement:设置参数
    • void batch(Statement statement):批量处理
    • <E> List<E> query(Statement statement, ResultHandler resultHandler):执行查询

流程图

  • 分析源码执行流程,画出对应的流程图,方便理解和记忆

Mybatis创建MapperProxy

  • 包括了读取全局配置文件和Mapper文件生成全局配置类、创建Mapper代理对象的详细过程

Mybaits执行查询

  • 主要描述了mybaits通过代理对象如何执行查询语句到对查询结果的处理过程的详细描述

Mybaits执行查询的简化版

  • 主要根据Mybatis中的重要的四大类,和SqlSession、TypeHandler进行简化描述,如下: