Mybatis源码阅读之--Mapper执行流程

心不动则不痛 提交于 2020-04-07 11:52:27

Mybatis的主要操作流程为:

1.使用SqlSessionFactory创建SqlSession

2.利用SqlSession获取Mapper

3.使用获取到的Mapper执行相应的增删改查的操作

本文介绍其中的第二步SqlSession如何获取到Mapper。

首先我们会调用SqlSession的geMapper方法获取Mapper,而SqlSession默认的实现类为DefaultSqlSession,找到DefaultSqlSession的源码,getMapper方法如下:

// DefaultSqlSession的getMapper方法  @Overridepublic <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}

通过以上代码,得知是从configuration中获取Mapper,继续进入configuration的getMapper方法--Configuration是Mybatis的配置类,里面记录了很多Mybatis的配置属性,其中就包括这里讲到的MapperRegistry

// Configuration的getMapper方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

也很简单,是通过mapperRegistry的getMapper方法获取到Mapper的,继续进入MapperRegistry类的getMapper方法:

  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
  // MapperRegistry的getMapper方法
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从knownMappers中拿到MapperProxyFactory
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
    // 通过mapperProxyFactory创建一个对象,当然这里是创建的Mapper代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

 

上述代码也就做了两个工作

1. 从knownMappers中拿到mapperProxyFactory,从名字来看就知道是Mapper代理的工厂类,作用也就是生产Mapper代理(这里就用到的是工厂方法模式)

2. 使用mapperProxyFactory的newInstance方法创建一个实例,并返回。

其中knowMappers也就是一个HashMap,里面存放了对应Mapper类型的MapperProxyFacory,也就是一个Mapper类有一个MapperProxyFactory,这两者是一对一的关系。

注意:MapperRegistry(Mapper注册表)的主要工作有两个,一个是这里提到的获取Mapper类,另一个是为每个Mapper注册MapperProxyFactory,这里暂且不谈。

下面分析MapperProxyFacory的newInstance方法

public class MapperProxyFactory<T> {
  // mapper 接口
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
  //....一些不重要的方法省略
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    // 利用Jdk的动态代理创建Mapper实例
    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);
  }
}

MapperProxyFactory方法利用JDK的动态代理创建了代理对象,其中InvocationHandler是MapperProxy,而MapperProxy的创建又使用到了methodCache,稍后会提到methodCache。

再进一步地查看MapperProxy,这个类实现了InvocationHandler接口,也就是Mapper的每个方法被调用时,都是调用MapperProxy的invoke方法,下面主要看看这个方法。

public class MapperProxy<T> implements InvocationHandler, Serializable {
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache;

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 调用从Object继承的方法,则直接调用,不做任何特殊的处理
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else { // 否则调用缓存的对应的方法
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }
  
  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      // 如果缓存中没有,则新建一个放到缓存中并返回
      return methodCache.computeIfAbsent(method, m -> {
        if (m.isDefault()) {
          try {
            if (privateLookupInMethod == null) {
              return new DefaultMethodInvoker(getMethodHandleJava8(method));
            } else {
              return new DefaultMethodInvoker(getMethodHandleJava9(method));
            }
          } catch (IllegalAccessException | InstantiationException | InvocationTargetException
              | NoSuchMethodException e) {
            throw new RuntimeException(e);
          }
        } else {
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }
}

invoke的思路也很简单明确,如果当前调用的方法是Object类声明的,直接调用,否则调用cachedInvoker方法,返回的MapperMethodInvoker对象,并调用此对象的invoke方法。

cachedInvoker方法就是从methodCache中拿一个MapperMethodInvoker,如果拿不到,就新建一个。新建时会查看当前调用的方法是不是又默认实现(m.isDefault()),如果是,就新建一个DefaultMethodInvoker,否则,新建一个PlainMethodInvoker,这两个类都是MethodInvoker的实现类,作用就是一个处理默认方法的调用,另一个处理没有被实现的方法的调用。

先看下DefaultMethodInvoker的实现:

  private static class DefaultMethodInvoker implements MapperMethodInvoker {
    private final MethodHandle methodHandle;

    public DefaultMethodInvoker(MethodHandle methodHandle) {
      super();
      this.methodHandle = methodHandle;
    }

    // 接口默认方法的处理,实际上就是调用了代理对象的同名方法,也就是那个默认实现的方法(感觉解释的好绕口啊)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return methodHandle.bindTo(proxy).invokeWithArguments(args);
    }
  }

它的主要作用就是调用代理对象的默认实现方法,其中用到了MethodHandle,我还不太明白,不考虑那些细节了。

接着看下PlainMethodInvoker:

  private static class PlainMethodInvoker implements MapperMethodInvoker {
    private final MapperMethod mapperMethod;

    public PlainMethodInvoker(MapperMethod mapperMethod) {
      super();
      this.mapperMethod = mapperMethod;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return mapperMethod.execute(sqlSession, args);
    }
  }

这个类就更简单了,直接调用mapperMethod的execute方法(创建PlainMethodInvoker需要传入一个MapperMethod对象)

通过上面DefaultMethodInvoker和PlainMethodInvoker可以看到面向接口编程的好处,不同的方法调用处理抽象成一个MethodInvoker,那么不同方法的处理就不用再写很多if...else...了,都统一为MethodInvoker的invoke方法。

最后就是分析MapperMethod对象的execute方法了,其实MapperMethod方法才是处理增删改查的主要类。

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        // 处理insert
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        // 处理update
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        // 处理delete
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        // 处理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;
  }

  private Object rowCountResult(int rowCount) {
    final Object result;
    if (method.returnsVoid()) {
      result = null;
    } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
      result = rowCount;
    } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
      result = (long)rowCount;
    } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
      result = rowCount > 0;
    } else {
      throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
    }
    return result;
  }

  private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
    MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
    if (!StatementType.CALLABLE.equals(ms.getStatementType())
        && void.class.equals(ms.getResultMaps().get(0).getType())) {
      throw new BindingException("method " + command.getName()
          + " needs either a @ResultMap annotation, a @ResultType annotation,"
          + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
    }
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
    } else {
      sqlSession.select(command.getName(), param, method.extractResultHandler(args));
    }
  }

  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectList(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }

  private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
    Cursor<T> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectCursor(command.getName(), param, rowBounds);
    } else {
      result = sqlSession.selectCursor(command.getName(), param);
    }
    return result;
  }
private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
    Map<K, V> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
    } else {
      result = sqlSession.selectMap(command.getName(), param, method.getMapKey());
    }
    return result;
  }
}

这个类的篇幅有点长,重点就看execute方法,它根据方法执行的sql命令(insert/delete/update/select),调用sqlSession的不同的方法(insert/delete/update/select等)。

其中insert/delete/update这三个命令的执行结果都进行了rowcount处理。

select方法有多种,selectOne/selectList/selectMap/selectCorsur;

select中两个特殊的参数:

1. resultHandler,若参数中有resultHandler时,调用select的有resultHandler版本,并返回null;

2. rowBound,用于行约束的,若指定了rowBound,那么mybatis会在取出的数据中,将约束的行放回,约束外的行不再返回。

综上把mapper的获取并调用的过程已经说完了,总结一下:

一:mapper的获取过程

1. sqlSession.getMapper获取mapper

2.configuration.getMapper获取mapper

3.mapperRegistry.getMapper获取mapper

4.通过MapperProxyFactory.newInstance方法创建mapper的代理对象。

  4.1 其中mapper的代理对象的创建使用JDK动态代理,Interfaces为mapper的interface,invocationHandler为MapperProxy对象。

  4.2 MapperProxy.invoke调用了MethodInvoker.invoke

  4.3 MethodInvoker.invoke又调用了MapperMethod.execute方法

  4.5 MapperMethod.execute根据sqlcommand调用sqlSession.select/update/delete/insert等方法

二:mapper的代理对象逻辑

BlogMapper.deleteBlog ->  MapperProxy.invoke -> MethodInvoker.invoke -> MapperMethod.execute -> SqlSession.insert/select/update/delete

最后补充一张时序图:

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!