Mybatis系列五:MapperRegistry注册并解析Mapper Class

Mybatis系列五:MapperRegistry注册并解析Mapper Class

概述

《Mybatis系列一:构建SqlSessionFactory》一文中了解了Mybatis配置文件的解析,其中对mappers元素解析使用到了MapperRegistryr这个类,现在分析下这个类是如何解析Mapper Class的。当然也用到XMLMapperBuilder解析Mapper XML配置文件。

// XMLMapperBuilder解析Mapper.xml配置文件 // MapperAnnotationBuilder解析Mapper接口,调用XMLMapperBuilder解析Mapper.xml配置文件 **** ****

Mapper.xml配置文件中的select|insert|update|delete元素解析成MappedStatement对象,保存Configuration#mappedStatements中。
Mapper.class注册到MapperRegistry#knownMappers中,映射一个MapperProxyFactory,为Mapper创建代理对象。

一、Configuration

1.1 回顾XMLConfigBuilder是如何解析mapper元素的

private void mapperElement(XNode parent) throws Exception {
   if (parent != null) {
     for (XNode child : parent.getChildren()) {
       if (“package”.equals(child.getName())) {
         String mapperPackage = child.getStringAttribute(“name”);
         configuration.addMappers(mapperPackage);
       } else {
         …
         if (resource != null && url == null && mapperClass == null) {
           …
         } else if (resource == null && url != null && mapperClass == null) {
           …
         } else if (resource == null && url == null && mapperClass != null) {
           Class<?> mapperInterface = Resources.classForName(mapperClass);
          ** configuration.addMapper(mapperInterface);**
         } else {
           …
         }
       }
     }
   }
 }

1.2 扫描指定包下的Mapper Class

public void addMappers(String packageName) {
   mapperRegistry.addMappers(packageName);
 }

1.3 添加Mapper Class

public void addMapper(Class type) {
   mapperRegistry.addMapper(type);
 }

二、MapperRegistry

2.1 扫描指定包下的Mapper Class

public void addMappers(String packageName) {
   addMappers(packageName, Object.class);
 }

2.2 扫描指定包下指定类型的Mapper Class

public void addMappers(String packageName, Class<?> superType) {
   ResolverUtil<Class<?>> resolverUtil = new ResolverUtil

2.3 添加Mapper Class

public void addMapper(Class type) {
   if (type.isInterface()) {
// 是否已经注册
     if (hasMapper(type)) {
       throw new BindingException(“Type " + type + " is already known to the MapperRegistry.”);
     }
     boolean loadCompleted = false;
     try {
       knownMappers.put(type, new MapperProxyFactory(type));
       // 必须在运行解析器之前//添加类型,否则//映射器解析器可能会自动尝试绑定。如果类型是已知的,则不会尝试。传送门《Mybatis系列六:MapperAnnotationBuilder解析Mapper Class》
       MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
       parser.parse();
       loadCompleted = true;
     } finally {
       if (!loadCompleted) {
         knownMappers.remove(type);
       }
     }
   }
 }

2.4 获取Mapper接口代理实例

DefaultSqlSession#getMapper调用的是Configuration#getMapper,Configuration#getMapper调用的是MapperRegistry#getMapper。
public T getMapper(Class type, SqlSession sqlSession) {
   // 获取Mapper接口对应的MapperProxyFactory
   final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
   if (mapperProxyFactory == null) {
     throw new BindingException(“Type " + type + " is not known to the MapperRegistry.”);
   }
   try {
// 创建Mapper接口实例
     return mapperProxyFactory.newInstance(sqlSession);
   } catch (Exception e) {
     throw new BindingException("Error getting mapper instance. Cause: " + e, e);
   }
 }

三、MapperProxyFactory

3.1 根据MapperProxy创建Mapper接口代理实例

protected T newInstance(MapperProxy mapperProxy) {
   return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
 }

3.2 根据SqlSession创建Mapper接口代理实例

public T newInstance(SqlSession sqlSession) {
   final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
   return newInstance(mapperProxy);
 }

四、MapperProxy

4.1 反射调用Mapper接口方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   try {
// Object方法直接反射调用
     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);
   }
   final MapperMethod mapperMethod = cachedMapperMethod(method);
   return mapperMethod.execute(sqlSession, args);
 }

4.2 缓存Mapper接口方法对应的MapperMethod

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

4.3 invokeDefaultMethod

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

4.4 isDefaultMethod

private boolean isDefaultMethod(Method method) {
   return (method.getModifiers()
       & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC
       && method.getDeclaringClass().isInterface();
 }

五、MapperMethod

5.1 构造函数

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
   this.command = new SqlCommand(config, mapperInterface, method);
   this.method = new MethodSignature(config, mapperInterface, method);
 }

5.2 SqlCommand

5.2.1 SqlCommand构造函数

public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
     final String methodName = method.getName();
     final Class<?> declaringClass = method.getDeclaringClass();
     MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
**         configuration);
     if (ms == null) {
  // 获取方法上的@Flush注解
       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();
  // select|insert|update|delete等
      ** type = ms.getSqlCommandType();

       if (type == SqlCommandType.UNKNOWN) {
         throw new BindingException("Unknown execution method for: " + name);
       }
     }
   }

5.2.2 根据方法完全限定名获取MappedStatement resolveMappedStatement

private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
       Class<?> declaringClass, Configuration configuration) {
// Mapper类完全限定名 + 方法名
     String statementId = mapperInterface.getName() + “.” + methodName;
// 判断是否存在对应的MappedStatement即select|insert|update|delete语句
     if (configuration.hasStatement(statementId)) {
       return configuration.getMappedStatement(statementId);
     } else if (mapperInterface.equals(declaringClass)) {
       return null;
     }

// 查询父接口上方法对应的MappedStatement
     for (Class<?> superInterface : mapperInterface.getInterfaces()) {
       if (declaringClass.isAssignableFrom(superInterface)) {
         MappedStatement ms = resolveMappedStatement(superInterface, methodName,
**             declaringClass, configuration)**;
         if (ms != null) {
           return ms;
         }
       }
     }
     return null;
   }

5.3 MethodSignature

5.3.1 解析方法参数为SQL使用的参数

public Object convertArgsToSqlCommandParam(Object[] args) {
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:6.2
     return paramNameResolver.getNamedParams(args);
   }

5.4 执行Mapper接口方法

调用sqlSession相应方法执行数据库操作。
public Object execute(SqlSession sqlSession, Object[] args) {
   Object result;
   switch (command.getType()) {
     case INSERT: {
// 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
     Object param = method.convertArgsToSqlCommandParam(args);
       result = rowCountResult(sqlSession.insert(command.getName(), param));
       break;
     }
     case UPDATE: {
  // 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
       Object param = method.convertArgsToSqlCommandParam(args);
       result = rowCountResult(sqlSession.update(command.getName(), param));
       break;
     }
     case DELETE: {
  // 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
       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 {
    // 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1

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

5.5 更新操作(insert|update|delete)结果转换

private Object rowCountResult(int rowCount) {
   final Object result;
   if (method.returnsVoid()) {
     result = null;
   } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
     result = rowCount;
   } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
     result = (long)rowCount;
   } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
     result = rowCount > 0;
   } else {
     throw new BindingException(“Mapper method '” + command.getName() + "’ has an unsupported return type: " + method.getReturnType());
   }
   return result;
 }

5.6 ResultHandler处理执行结果

private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
   MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
   if (!StatementType.CALLABLE.equals(ms.getStatementType())
       && void.class.equals(ms.getResultMaps().get(0).getType())) {
     throw new BindingException(“method " + command.getName()
         + " needs either a @ResultMap annotation, a @ResultType annotation,”
         + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
   }
   // 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
   Object param = method.convertArgsToSqlCommandParam(args);
   if (method.hasRowBounds()) {
// 分页查询
     RowBounds rowBounds = method.extractRowBounds(args);
     sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
   } else {
     sqlSession.select(command.getName(), param, method.extractResultHandler(args));
   }
 }

5.7 查询结果多条处理

private Object executeForMany(SqlSession sqlSession, Object[] args) {
   List result;
   // 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
   Object param = method.convertArgsToSqlCommandParam(args);
   if (method.hasRowBounds()) {
// 分页查询
     RowBounds rowBounds = method.extractRowBounds(args);
     result = sqlSession.selectList(command.getName(), param, rowBounds);
   } else {
     result = sqlSession.selectList(command.getName(), param);
   }
   // 实际返回类型不是List需要转换
   if (!method.getReturnType().isAssignableFrom(result.getClass())) {
     if (method.getReturnType().isArray()) {
       return convertToArray(result);
     } else {
       return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
     }
   }
   return result;
 }

// 转换成数组
private Object convertToArray(List list) {
   Class<?> arrayComponentType = method.getReturnType().getComponentType();
   Object array = Array.newInstance(arrayComponentType, list.size());
   if (arrayComponentType.isPrimitive()) {
     for (int i = 0; i < list.size(); i++) {
       Array.set(array, i, list.get(i));
     }
     return array;
   } else {
     return list.toArray((E[])array);
   }
 }

// 转换成实际返回集合类型
private Object convertToDeclaredCollection(Configuration config, List list) {
   // 这里用到了ObjectFactory
   Object collection = config.getObjectFactory().create(method.getReturnType());
   MetaObject metaObject = config.newMetaObject(collection);
   metaObject.addAll(list);
   return collection;
 }

5.8 查询结果类型Map

private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
   Map<K, V> result;
   // 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
   Object param = method.convertArgsToSqlCommandParam(args);
   if (method.hasRowBounds()) {
// 分页查询
     RowBounds rowBounds = method.extractRowBounds(args);
     result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
   } else {
     result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
   }
   return result;
 }

5.9 查询结果类型Cursor

private Cursor executeForCursor(SqlSession sqlSession, Object[] args) {
   Cursor result;
   // 使用注解或者多于1个参数返回Map<String, Object>,传送门:5.3.1
   Object param = method.convertArgsToSqlCommandParam(args);
   if (method.hasRowBounds()) {
     RowBounds rowBounds = method.extractRowBounds(args);
     result = sqlSession.selectCursor(command.getName(), param, rowBounds);
   } else {
     result = sqlSession.selectCursor(command.getName(), param);
   }
   return result;
 }

六、ParamNameResolver

6.1 构造函数

public ParamNameResolver(Configuration config, Method method) {
   final Class<?>[] paramTypes = method.getParameterTypes();
   // 获取参数注解
   final Annotation[][] paramAnnotations = method.getParameterAnnotations();
   final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
   int paramCount = paramAnnotations.length;
   // get names from @Param annotations
   for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
// 参数实现了RowBounds、ResultHandler接口忽略不处理
     if (isSpecialParameter(paramTypes[paramIndex])) {
       continue;
     }
     String name = null;
// 获取参数上的@Param注解,得到可以在SQL中使用的参数名
     for (Annotation annotation : paramAnnotations[paramIndex]) {
       if (annotation instanceof Param) {
         hasParamAnnotation = true;
         name = ((Param) annotation).value();
         break;
       }
     }
     if (name == null) {
       // 获取真实参数名
       if (config.isUseActualParamName()) {
         name = getActualParamName(method, paramIndex);
       }
       if (name == null) {
         // 使用参数位置索引
         name = String.valueOf(map.size());
       }
     }
     map.put(paramIndex, name);
   }
  
   // 参数未知索引与参数名映射关系
   names = Collections.unmodifiableSortedMap(map);
 }

private static boolean isSpecialParameter(Class<?> clazz) {
   return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
 }

6.2 获取参数名 getNamedParams

既可以通过@Param指定的参数名获取参数,也可以通过param1, param2, …获取参数。
public Object getNamedParams(Object[] args) {
   final int paramCount = names.size();
   if (args == null || paramCount == 0) {
     return null;
   } else if (!hasParamAnnotation && paramCount == 1) {
     return args[names.firstKey()];
   } else {
     final Map<String, Object> param = new ParamMap();
     int i = 0;
     for (Map.Entry<Integer, String> entry : names.entrySet()) {
  // 参数名与参数值映射关系
       param.put(entry.getValue(), args[entry.getKey()]);
       // add generic param names (param1, param2, …)
       final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
       // 确保不覆盖@Param注解命名的参数
       if (!names.containsValue(genericParamName)) {
    // param1, param2, …与参数值映射关系
         param.put(genericParamName, args[entry.getKey()]);
       }
       i++;
     }
     return param;
   }
 }

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值