MyBatis源码分析-MapperProxy

在MyBatis中,Mapper是一个接口,不是实体类。而在Java中,接口是无法运行的,所以MyBatis运用了动态代理模式来运行Mapper接口。

关于动态代理,可以看这篇 深度解析JDK动态代理

MyBatis首先创建了一个MapperProxy类,实现了InvocationHandler接口。就相当于上篇文章的MyHandler类。

由于动态代理只能代理有接口实现的类。需要传入要代理的接口,所以传入sqlSession.就相当于上篇文章的People接口。



既然是代理,就有invoke方法。入参method是真实对象要实现的业务方法。


    1.先判断是否是class类,

method.getDeclaringClass() //Java解释 返回由此method对象表示的方法的类的Class对象,其实就是判断method所在对象的类是哪种类型,是interface还是class.
    2.判断是否是默认方法
isDefaultMethod(method) //是Java8 新引入的语言特性 默认方法

    默认方法,主要为了解决接口跟实现类耦合太紧密的问题。 如果要在接口中新增一个方法,需要改动所有的实现类,改动太大。所以Java8新增了默认方法。只需要在接口方法中用default。

interface InterfaceA {
    default void foo() {
        System.out.println("InterfaceA foo");
    }
}
 继承方式有3种,可以选择不重写、重写继续声明为default、重写去掉default,声明为抽象方法。

    3.生成MapperMethod对象

      维护了一个map类型的缓存。缓存中没有则new一个MapperMethod出来。

 

    4.调用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: {
      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);
        }
        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;
  }

其中Sqlcommand表示数据库执行的一个 Transact-SQL 语句或存储过程

怎么去拿,需要进入SqlCommand。而SqlCommand是一个内部类。

    

 public static class SqlCommand {

    private final String name;
    private final SqlCommandType type;

    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      final String methodName = method.getName();
      final Class<?> declaringClass = method.getDeclaringClass();
       //这里是去获取MappedStatment。主要逻辑所在
      MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
          configuration);
      if (ms == null) {
        if (method.getAnnotation(Flush.class) != null) {
          name = null;
          type = SqlCommandType.FLUSH;
        } else {
          throw new BindingException("Invalid bound statement (not found): "
              + mapperInterface.getName() + "." + methodName);
        }
      } else {
        name = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
          throw new BindingException("Unknown execution method for: " + name);
        }
      }
    }

    private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
        Class<?> declaringClass, Configuration configuration) {
    //statementId 就是唯一标识,接口名+方法名,这里就是需要xml的命名空间需要跟接口匹配的原因。
      String statementId = mapperInterface.getName() + "." + methodName;
     //能匹配到statementId,就可以拿到MappedStatement。这里为什么要用configuration去匹配,
     // 是因为configuration里配置了扫描xml文件的路径,能够拿到所有的xml文件,进而拿到所有的statement.
      if (configuration.hasStatement(statementId)) {
        return configuration.getMappedStatement(statementId);
      } else if (mapperInterface.equals(declaringClass)) {
        return null;
      }
     //用了递归,循环获取。
      for (Class<?> superInterface : mapperInterface.getInterfaces()) {
        if (declaringClass.isAssignableFrom(superInterface)) {
          MappedStatement ms = resolveMappedStatement(superInterface, methodName,
              declaringClass, configuration);
          if (ms != null) {
            return ms;
          }
        }
      }
      return null;
    }
  }







猜你喜欢

转载自blog.csdn.net/Damon__Wang/article/details/80223581