Sql绑定

在mybatis中定义一个接口,然后在mapper.xml文件中编写一个sql语句,在执行该接口中方法的时候就会执行该sql语句,这是怎么做到的呢?

<!-- more -->

public interface UserMapper{
  public User getUser(int i);// 在mapper.xml中写一个<select>标签,id为getUser
}

MapperRegistry

MapperRegistry类是Mapper接口及其对应的代理对象工厂的注册中心。

// 全局配置
private final Configuration config;
// 记录Mapper接口和代理工厂之间的关系
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();

在mybatis初始化时读取配置文件以及Mapper接口的注解信息,调用MapperRegistry.addMapper()方法填充knownMappers。

public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (this.hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                this.knownMappers.put(type, new MapperProxyFactory(type));
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }
            }
        }
    }

在需要执行某个SQL时,会先调用MapperRegistry.getMapper()方法获取实现Mapper接口的代理对象

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        } else {
            try {
                return mapperProxyFactory.newInstance(sqlSession);
            } catch (Exception var5) {
                throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
            }
        }
    }

MapperProxyFactory

MapperProxyFactory负责为mapper接口创建代理对象MapperProxy

// 接口
private final Class<T> mapperInterface;
// 缓存,key为mapperInterface接口中某方法所对应的Method对象,value是对应的代理
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap();
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);
}

MapperProxy

实现了InvocationHandler接口,是接口的代理类,主要看invoke()方法,如果是接口,会调用cachedInvoker()方法生成代理类MapperMethodInvoker去执行

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -4724728412955527868L;
  private static final int ALLOWED_MODES = MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
      | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC;
  private static final Constructor<Lookup> lookupConstructor;
  private static final Method privateLookupInMethod;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  static {
    Method privateLookupIn;
    try {
      //java8的MethodHandles中没有privateLookupIn方法  privateLookupInMethod会是null
      privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
    } catch (NoSuchMethodException e) {
      privateLookupIn = null;
    }
    privateLookupInMethod = privateLookupIn;

    Constructor<Lookup> lookup = null;
    if (privateLookupInMethod == null) {
      // JDK 1.8
      try {
        lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
        lookup.setAccessible(true);
      } catch (NoSuchMethodException e) {
        throw new IllegalStateException(
            "There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.",
            e);
      } catch (Exception e) {
        lookup = null;
      }
    }
    lookupConstructor = lookup;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      // 是代理对象,直接执行invoke方法
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else { // 如果是接口不是代理对象的话,就会生成MapperMethodInvoker对象
        return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      // A workaround for https://bugs.openjdk.java.net/browse/JDK-8161372
      // It should be removed once the fix is backported to Java 8 or
      // MyBatis drops Java 8 support. See gh-1929
      MapperMethodInvoker invoker = methodCache.get(method);
      if (invoker != null) {
        return invoker;
      }

      return methodCache.computeIfAbsent(method, m -> {
        // 默认方法(Java8之后出的默认方法)
        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;
    }
  }

  private MethodHandle getMethodHandleJava9(Method method)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    final Class<?> declaringClass = method.getDeclaringClass();
    return ((Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup())).findSpecial(
        declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
        declaringClass);
  }

  private MethodHandle getMethodHandleJava8(Method method)
      throws IllegalAccessException, InstantiationException, InvocationTargetException {
    final Class<?> declaringClass = method.getDeclaringClass();
    return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
  }

  interface MapperMethodInvoker {
    Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
  }
}
MapperMethodInvoker

MapperMethodInvoker有两个实现类,PlainMethodInvoker和DefaultMethodInvoker

// 普通的接口方法会执行
//PlainMethodInvoker只是对于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);
    }
  }

// java8中的默认方法会执行
  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);
    }
  }
MapperMethod

MapperMethod中封装了Mapper接口对应的方法的信息,以及对应SQL语句的信息

// 记录了SQL语句的名称和类型
private final MapperMethod.SqlCommand command;
// 记录了Mapper接口中对应方法的相关信息
private final MapperMethod.MethodSignature method;

SqlCommandType记录了SQL语句的类型

public enum SqlCommandType {
    UNKNOWN,
    INSERT,
    UPDATE,
    DELETE,
    SELECT,
    FLUSH;

    private SqlCommandType() {
    }
}

根据PlainMethodInvoker中invoke方法发现,调用的是MapperMethodexecute方法

public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        Object param;
              // 根据SQL类型调用不同的sqlsession的方法
        switch(this.command.getType()) {
        case INSERT:
            // 使用ParamNameResolver处理参数列表,将实参与指定参数名对应起来
            param = this.method.convertArgsToSqlCommandParam(args);
            // rowCountResult会根据method字段记录的方法的返回值类型对结果进行转换
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            // 处理返回值为void且ResultSet通过ResultHandler处理的方法
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } 
            // 处理返回值为集合的方法
            else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } 
            // 处理返回值为map的方法
            else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } 
            // 处理返回值为Cursor的方法
            else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } 
            // 处理返回值为单一对象的方法
            else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }
https://zhhll.icu/2020/框架/mybatis/组件分析/7.mybatis之Sql绑定/

本文由mdnice多平台发布


bug生产者
20 声望0 粉丝