MyBatis类型安全的Mapper执行实现原理概述

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lichunericli/article/details/82558573

MyBatis的执行流程通常可分为两种:
(1)直接执行已映射的SQL语句
   String statement = "com.XXX.dao.UserDao.getById";
   User user = session.selectOne(statement, 1);
(2)执行更清晰和类型安全的代码
   UserDao userDao = session.getMapper(UserDao.class);   
   User user = userDao.getById(1);   
在MyBatis中是通过MapperProxy动态代理Mapper的,对Mapper中的方法的处理是通过MapperProxy实现的。

1. MapperProxy的获取
在DefaultSqlSession的源码中可以看出Mapper的获取是通过Configuration来实现的:
public <T> T getMapper(Class<T> type) {
  return configuration.<T>getMapper(type, this);
}
在Configuration的源码中可以看出Mapper的获取是通过MapperRegistry来实现的:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}
MyBatis的MapperRegistry类实例在应用启动时创建,在MapperRegistry中通过knownMappers属性来获取MapperProxyFactory,然后通过MapperProxyFactory来获取MapperProxy,其实现源码如下所示:
Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  return mapperProxyFactory.newInstance(sqlSession);
}
public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface },
mapperProxy);
}

添加Mapper或获取Mapper都可以在MapperRegistry类中实现的,Mapper的注册必须是接口而且不能重复添加,Mapper先注册到knownMappers中,然后通过MapperAnnotationBuilder来进行解析,如果解析异常则从knownMappers中移除当前Mapper。

由MapperProxyFactory来获取Mapper实例是通过JDK自带的动态代理实现的。

通过代理后所有Mapper的方法调用时都会调用MapperProxy的invoke方法,但是并不是任何方法都需要执行调用代理对象来进行执行,例如Object中通用的方法就无需执行。

2. MapperMethod的执行
由MapperProxy源码可知invoke方法的执行是MapperMethod实现的,先从缓存中获取MapperMethod,获取不到再重新生成:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  if (Object.class.equals(method.getDeclaringClass())) {
    return method.invoke(this, args);
  } else if (isDefaultMethod(method)) {
    return invokeDefaultMethod(proxy, method, args);
  }
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}

MapperMethod中的execute执行方法:
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: {
      result = rowCountResult(sqlSession.update(command.getName(), param));
    }
    case DELETE: {
      result = rowCountResult(sqlSession.delete(command.getName(), param));
    }
    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 {
        result = sqlSession.selectOne(command.getName(), param);
      }
    case FLUSH:
      result = sqlSession.flushStatements();
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  return result;
}

MapperMethod中存在SqlCommand静态内部类,通过SqlCommand来判断是CRUD类型的哪种,然后根据类型来调用SqlSession中的执行方法。由源码分析可知无论选择MyBatis的哪种执行流程,其底层实现都是由SqlSession来完成。

猜你喜欢

转载自blog.csdn.net/lichunericli/article/details/82558573