第2篇 Mapper接口方法执行分析

事例:

 UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 User user = mapper.selectUser(2);

代码分析

1、 sqlSession#getMapper

该方法返回的是一个代理类MapperProxy。方法调用过程是这样的:
sqlSession#getMapper->configuration#getMapper->mapperRegistry#getMapper->mapperProxyFactory#newInstance
最后是由工厂类mapperProxyFactory创建的。

// sqlSession#getMapper
 public  T getMapper(Class type) {
    return configuration.getMapper(type, this);
  }

// MapperRegistry
  public  T getMapper(Class type, SqlSession sqlSession) {

// 配置解析的时候,就已经把mapper接口存储map集合knownMappers中了
    final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }
// MapperProxyFactory
 public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

2、MapperProxy#invoke

接口方法的执行其实就是进入到MapperProxy#invoke方法中。我们直接来看此方法。

// MapperProxy#invoke
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
    /**
    * 代理类中会继承Object类的方法,例如:toString、equals
    * 这类方法不进行代理,直接执行了
    */
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else {
 
  /**
    * 创建MapperMethodInvoker
    * 然后调用invoke
    */
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

1、创建MapperMethodInvoker

// MapperProxy#cachedInvoker
 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;
    }
  }

3、MapperMethodInvoker#invoke

此处我们分析PlainMethodInvoker这实现类的invoke方法,后面我们在讨论PlainMethodInvoker和DefaultMethodInvoker的区别。
mybatis中很多地方都是类包着类,一层裹一层。这里MapperMethodInvoker包了
MapperMethod,具体的操作都是MapperMethod来执行的。

  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);
    }
  }

4、MapperMethod#execute

4.1、MapperMethod

MapperMethod顾名思义就是Mapper接口要执行的方法。从构造函数来看:它持有mapper接口,执行方法,配置类的引用,然后,根据这三个参数构建了SqlCommand和MethodSignature。

public class MapperMethod {
/**
*sql命令相关
*/
  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);
  }
}

a) SqlCommand
该类中有两个属性:
name:可以定位mapper.xml中的CRUD方法。
SqlCommandType :要执行的是哪一中CRUD操作。
在MapperMethod#execute中发挥了作用!

 public static class SqlCommand {
 // MappedStatement的id,
// 例如com.gupaoedu.lsj.mybatis.mapper.UserMapper.selectUser
    private final String name;
// enum类型:UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
    private final SqlCommandType type;
}

b) MethodSignature
它是对接口执行的方法作了分析,包括它的返回值类型,返回的是多条还是单条,是否有mapKey等。在MapperMethod#execute中发挥了作用!

 public static class MethodSignature {

    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final boolean returnsCursor;
    private final boolean returnsOptional;
    private final Class returnType;
    private final String mapKey;
    private final Integer resultHandlerIndex;
    private final Integer rowBoundsIndex;
    private final ParamNameResolver paramNameResolver;
}

4.2、execute方法执行

事例中,我执行的是单条查询方法,所以代码会进入到case SELECT这个分支。
最终会调用sqlSession#selectOne->sqlSession#selectList
接下来的分析就是这篇文章讲解的了。
selectList执行分析

// MapperMethod
 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);
    /**
     * 最后这里进行了查询,接下来的分析就是这篇文章讲解的了。
     * (https://www.jianshu.com/p/19b508e7b5c7)
    */
          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;
  }

总结:

1.mybatis最开始方法的执行是采用namespace+id的方式,即
sqlSession.selectOne("com.gupaoedu.lsj.mybatis.mapper.UserMapper.selectUser",1)
2.后来增加了mapper接口的调用,但是最终都会转化成第一种方式执行
sqlSession.getMapper(UserMapper.class);

你可能感兴趣的:(第2篇 Mapper接口方法执行分析)