当前位置 主页 > 网站技术 > 代码类 >

    MyBatis执行Sql的流程实例解析(2)

    栏目:代码类 时间:2019-12-23 21:08

    下面是动态代理类MapperProxy,调用Mapper接口的所有方法都会先调用到这个代理类的invoke方法(注意由于Mybatis中的Mapper接口没有实现类,所以MapperProxy这个代理对象中没有委托类,也就是说MapperProxy干了代理类和委托类的事情)。好了下面重点看下invoke方法。

    //MapperProxy代理类
    public class MapperProxy<T> implements InvocationHandler, Serializable {
    
     private static final long serialVersionUID = -6424540398559729838L;
     private final SqlSession sqlSession;
     private final Class<T> mapperInterface;
     private final Map<Method, MapperMethod> methodCache;
    
     public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
      this.sqlSession = sqlSession;
      this.mapperInterface = mapperInterface;
      this.methodCache = methodCache;
     }
    
     @Override
     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 (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
       }
      } catch (Throwable t) {
       throw ExceptionUtil.unwrapThrowable(t);
      }
      //获取MapperMethod,并调用MapperMethod
      final MapperMethod mapperMethod = cachedMapperMethod(method);
      return mapperMethod.execute(sqlSession, args);
     }
    
     private MapperMethod cachedMapperMethod(Method method) {
      MapperMethod mapperMethod = methodCache.get(method);
      if (mapperMethod == null) {
       mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
       methodCache.put(method, mapperMethod);
      }
      return mapperMethod;
     }
    
     @UsesJava7
     private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
       throws Throwable {
      final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
        .getDeclaredConstructor(Class.class, int.class);
      if (!constructor.isAccessible()) {
       constructor.setAccessible(true);
      }
      final Class<?> declaringClass = method.getDeclaringClass();
      return constructor
        .newInstance(declaringClass,
          MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
            | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
        .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
     }
    
     /**
      * Backport of java.lang.reflect.Method#isDefault()
      */
     private boolean isDefaultMethod(Method method) {
      return ((method.getModifiers()
        & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC)
        && method.getDeclaringClass().isInterface();
     }
    }

    所以这边需要进入MapperMethod的execute方法:

    public Object execute(SqlSession sqlSession, Object[] args) {
      Object result;
      //判断是CRUD那种方法
      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;
     }