MYBATIS代理封装 binding模块
这个模块有四个类,这四个类是层层调用的关系,对外的是MapperRegistry,映射器注册器。它会被Configuration类直接调用,用于将用户自定义的映射器全部注册到注册器中,而这个注册器显而易见会保存在Configuration实例中备用。
其实看到这个名称,我们就会想起之前解析的类型别名注册器与类型处理器注册器,其实他们之间的目的差不多,就是注册的内容不同罢了,映射器注册器注册的是MyBatis使用者自定义的各种映射器。
MapperRegistry类
MapperRegistry是Mapper接口及其对应的代理对象工厂的注册中心
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 public class MapperRegistry { private final Configuration config; private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap <>(); public MapperRegistry (Configuration config) { this .config = config; } public <T> T getMapper (Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null ) { throw new BindingException ("Type " + type + " is not known to the MapperRegistry." ); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException ("Error getting mapper instance. Cause: " + e, e); } } public <T> boolean hasMapper (Class<T> type) { return knownMappers.containsKey(type); } public <T> void addMapper (Class<T> 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)); MapperAnnotationBuilder parser = new MapperAnnotationBuilder (config, type); parser.parse(); loadCompleted = true ; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } public Collection<Class<?>> getMappers() { return Collections.unmodifiableCollection(knownMappers.keySet()); } public void addMappers (String packageName, Class<?> superType) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil <>(); resolverUtil.find(new ResolverUtil .IsA(superType), packageName); Set<Class<? extends Class <?>>> mapperSet = resolverUtil.getClasses(); for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } } public void addMappers (String packageName) { addMappers(packageName, Object.class); } }
上面的集合knownMappers中键值的类型分别为Class类型与MapperProxyFactory类型,MapperProxyFactory是映射器代理工厂,通过这个工厂类可以获取到对应的映射器代理类MapperProxy,这里只需要保存一个映射器的代理工厂,根据工厂就可以获取到对应的映射器。
注册器中必然定义了添加映射器和获取映射器的方法来对外提供服务(供外部调取)。这里就是addMapper()方法与getMapper()方法,在该注册器中还有一种根据包名来注册映射器的方法addMappers()方法。因为该方法最后会调用addMapper()方法来完成具体的注册功能
MapperProxyFactory类
主要负责创建代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class MapperProxyFactory <T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap <>(); public MapperProxyFactory (Class<T> mapperInterface) { this .mapperInterface = mapperInterface; } public Class<T> getMapperInterface () { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache () { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance (MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class []{mapperInterface}, mapperProxy); } public T newInstance (SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy <>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
该类内部先调用MapperProxy的构造器生成MapperProxy映射器代理实例,然后以之使用JDK自带的动态代理来生成映射器代理实例
在这个代理工厂中定义了一个缓存集合,其实为了调用MapperProxy的构造器而设,这个缓存集合用于保存当前映射器中的映射方法的。
映射方法单独定义,是因为这里并不存在一个真正的类和方法供调用,只是通过反射和代理的原理来实现的假的调用,映射方法是调用的最小单位(独立个体),将映射方法定义之后,它就成为一个实实在在的存在,我们可以将调用过的方法保存到对应的映射器的缓存中,以供下次调用,避免每次调用相同的方法的时候都需要重新进行方法的生成。很明显,方法的生成比较复杂,会消耗一定的时间,将其保存在缓存集合中备用,可以极大的解决这种时耗问题。
即使是在一般的项目中也会存在很多的映射器,这些映射器都要注册到注册器中,注册器集合中的每个映射器中都保存着一个独有的映射器代理工厂实例,而不是映射器实例,映射器实例只在需要的时候使用代理工厂进行创建,所以我们可以这么来看,MapperProxyFactory会存在多个实例,针对每个映射器有一个实例,这个实例就作为值保存在注册器中,而下一节中的MapperProxy被MapperProxyFactory调用来生成代理实例,同样也是与映射器接口一一对应的存在(即存在多个实例,只不过这个实例只会在需要的时候进行创建,不需要的时候是不存在的)。
MapperProxy类
实现了InvocationHandler接口,是代理对象的核心逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 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 (method.isDefault()) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod (Method method) { return methodCache.computeIfAbsent(method, k -> new MapperMethod (mapperInterface, method, sqlSession.getConfiguration())); } 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); } }
该类中的三个参数
sqlSession:session会话
mapperInterface:映射器接口
methodCache:方法缓存
以上三个参数需要在构造器中进行赋值,首先session会话用于指明操作的来源,映射器接口指明操作的目标,方法缓存则用于保存具体的操作方法实例。在每个映射器代理中都存在以上三个参数,也就是说我们一旦我们使用过某个操作,那么这个操作过程中产生的代理实例将会一直存在,且具体操作方法会保存在这个代理实例的方法缓存中备用。
MapperProxy是使用JDK动态代理实现的代理功能,其重点就在invoke()方法中,首先过滤掉Object类的方法,然后从先从缓存中获取指定的方法,如果缓存中不存在则新建一个MapperMethod实例并将其保存在缓存中,如果缓存中存在这个指定的方法实例,则直接获取执行。
这里使用缓存进行流程优化,极大的提升了MyBatis的执行速率。
MapperMethod
封装了Mapper接口与中对应的信息,以及对应sql语句的信息。也就是说MapperMethod是连接Mapper接口欧以及配置文件中定义的sql语句的桥梁
映射器方法是最底层的被调用者,同时也是binding模块中最复杂的内容。它是MyBatis中对SqlSession会话操作的封装,那么这意味着什么呢?意味着这个类可以看做是整个MyBatis中数据库操作的枢纽,所有的数据库操作都需要经过它来得以实现。
我们单独使用MyBatis时,有时会直接操作SqlSession会话进行数据库操作,但是在SSM整合之后,这个数据库操作却是自动完成的,那么Sqlsession就需要被自动执行,那么组织执行它的就是这里的MapperMethod。
SqlSession会作为参数在从MapperRegisty中getMapper()中一直传递到MapperMethod中的execute()方法中,然后在这个方法中进行执行。
结构信息 1 2 3 4 5 6 7 8 9 10 11 12 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); } }
这两个字段类型都是以MapperMethod中的静态内部类的方式定义的,分别表示sql命令与接口中的方法。
这两个字段都是final修饰,表示不可变,即一旦赋值,就永远是该值。
execute方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 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); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } 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; }
这里可以很明显的看出,在这个执行方法中封装了对SqlSession的操作,而且将所有的数据库操作分类为增删改查四大类型,分别通过调用SqlSession的4大类方法来完成功能。
param表示的是SQL执行参数,可以通过静态内部类MethodSignature的convertArgsToSqlCommandParam()方法获取。
对SqlSession的增、删、改操作使用了rowCountResult()方法进行封装,这个方法对SQL操作的返回值类型进行验证检查,保证返回数据的安全。
针对SqlSession的查询操作较为复杂,分为多种情况:
针对拥有结果处理器的情况:执行executeWithResultHandler(SqlSession sqlSession, Object[] args)方法这种有结果处理器的情况,就不需要本方法进行结果处理,自然有指定的结果处理器来进行处理,所以其result返回值设置为null。
针对返回多条记录的情况:执行executeForMany(SqlSession sqlSession, Object[] args)方法内部调用SqlSession的selectList(String statement, Object parameter, RowBounds rowBounds)方法针对Collections以及arrays进行支持,以解决#510BUG
针对返回Map的情况:执行executeForMap(SqlSession sqlSession, Object[] args)方法内部调用SqlSession的selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds)方法
针对Cursor的返回情况:执行executeForCursor(SqlSession sqlSession, Object[] args)方法针对游标处理。
针对返回一条记录的情况:执行SqlSession的selectOne(String statement, Object parameter)方法
针对前三种情况返回的都不是一条数据,在实际项目必然会出现需要分页的情况,MyBatis中为我们提供了RowBounds来进行分页设置,在需要进行分页的情况,直接将设置好的分页实例传到SqlSession中即可。只不过这种方式的分页属于内存分页,针对数据量小的情况比较适合,对于大数据量的查询分页并不适合,大型项目中的分页也不会使用这种方式来实现分页,而是采用之后会解析的分页插件来时限物理分页,即直接修改SQL脚本来实现查询级的分页,再不会有大量查询数据占据内存。
rowCountResult方法
返回影响条数,将sql插入等方法返回的int转为mapper中方法需要的返回类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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; }
executeWithResultHandler方法
结果处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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." ); } 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)); } }
多条结果处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private <E> Object executeForMany (SqlSession sqlSession, Object[] args) { List<E> result; 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); } if (!method.getReturnType().isAssignableFrom(result.getClass())) { if (method.getReturnType().isArray()) { return convertToArray(result); } else { return convertToDeclaredCollection(sqlSession.getConfiguration(), result); } } return result; }
map 结果处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private <K, V> Map<K, V> executeForMap (SqlSession sqlSession, Object[] args) { Map<K, V> result; Object param = method.convertArgsToSqlCommandParam(args); if (method.hasRowBounds()) { RowBounds rowBounds = method.extractRowBounds(args); result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds); } else { result = sqlSession.selectMap(command.getName(), param, method.getMapKey()); } return result; }
executeForCursor方法
游标处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private <T> Cursor<T> executeForCursor (SqlSession sqlSession, Object[] args) { Cursor<T> result; 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; }
SqlCommand静态内部类
MapperMethod的内部类,使用name字段记录sql语句的名称,type字段记录sql语句的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 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(); 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); } } } public String getName () { return name; } public SqlCommandType getType () { return type; } private MappedStatement resolveMappedStatement (Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) { String statementId = mapperInterface.getName() + "." + methodName; 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 ; } }
这个内部类比较简单,是对SQL命令的封装,定义两个字段,name和type,前者表示SQL命令的名称,这个名称就是接口的全限定名+方法名称(中间以.连接),后者表示的是SQL命令的类型,无非增删改查四种。
内部类中有一个带参数的构造器用于对字段赋值,里面涉及到了MappedStatement类,这个类封装的是映射语句的信息,在构建Configuration实例时会创建一个Map集合用于存储所有的映射语句,而这些映射语句的解析存储是在构建映射器的时候完成的(MapperBuilderAssistant类中)。
MethodSignature 静态内部类
也是其内部类,封装了Mapper接口中定义的方法的相关信息
结构信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public static class MethodSignature { private final boolean returnsMany; private final boolean returnsMap; private final boolean returnsVoid; private final boolean returnsCursor; private final boolean returnsOptional; private final Class<?> returnType; private final String mapKey; private final Integer resultHandlerIndex; private final Integer rowBoundsIndex; private final ParamNameResolver paramNameResolver; public MethodSignature (Configuration configuration, Class<?> mapperInterface, Method method) { Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface); if (resolvedReturnType instanceof Class<?>) { this .returnType = (Class<?>) resolvedReturnType; } else if (resolvedReturnType instanceof ParameterizedType) { this .returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType(); } else { this .returnType = method.getReturnType(); } this .returnsVoid = void .class.equals(this .returnType); this .returnsMany = configuration.getObjectFactory().isCollection(this .returnType) || this .returnType.isArray(); this .returnsCursor = Cursor.class.equals(this .returnType); this .returnsOptional = Optional.class.equals(this .returnType); this .mapKey = getMapKey(method); this .returnsMap = this .mapKey != null ; this .rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class); this .resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class); this .paramNameResolver = new ParamNameResolver (configuration, method); } }
构造器的参数分别为Configuration与Method,我们的字段的值部分是需要从这两个参数中获取的。
返回类型returnType从Method中获取
hasNamedParameters表示是否存在注解方式定义的参数
获取Method中RowBounds类型参数与ResultHandler类型参数的位置
convertArgsToSqlCommandParam方法
这个方法是中转方法最终调用的是paramNameResolver类
1 2 3 public Object convertArgsToSqlCommandParam (Object[] args) { return paramNameResolver.getNamedParams(args); }
paramNameResolver类
主要用来处理接口形式的参数,最后会把参数处放在一个map中map的key为参数的位置,value为参数的名字
结构信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class ParamNameResolver { private static final String GENERIC_NAME_PREFIX = "param" ; private final SortedMap<Integer, String> names; private boolean hasParamAnnotation; public ParamNameResolver (Configuration config, Method method) { final Class<?>[] paramTypes = method.getParameterTypes(); final Annotation[][] paramAnnotations = method.getParameterAnnotations(); final SortedMap<Integer, String> map = new TreeMap <>(); int paramCount = paramAnnotations.length; for (int paramIndex = 0 ; paramIndex < paramCount; paramIndex++) { if (isSpecialParameter(paramTypes[paramIndex])) { continue ; } String name = null ; 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); }
构造方法的会经历如下的步骤
通过反射得到方法的参数类型和方法的参数注解注解,method.getParameterAnnotations()方法返回的是注解的二维数组,每一个方法的参数包含一个注解数组。
遍历所有的参数
首先判断这个参数的类型是否是特殊类型,RowBounds和ResultHandler,是的话跳过,咱不处理
判断这个参数是否是用来Param注解,如果使用的话name就是Param注解的值,并把name放到map中,键为参数在方法中的位置,value为Param的值
如果没有使用Param注解,判断是否开启了UseActualParamName,如果开启了,则使用java8的反射得到方法的名字,此处容易造成异常, 具体原因参考上一篇博文.
如果以上条件都不满足的话,则这个参数的名字为参数的下标
getNamedParams方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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()]); final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1 ); if (!names.containsValue(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } return param; } }
总结
mybatis中使用Binding模块对Mapper代理接口的产生进行包装。MapperRegistry用于Mapper接口的注册和代理Mapper接口的产生,然后由MapperProxyFactory创建代理对象MapperProxy,MapperProxy实现了InvocationHandler接口,是代理对象的核心逻辑,当客户端调用代理对象的方法时候就会调用MapperProxy对象的invoke方法完成动态代理过程。
调用invoke方法后最终执行的是MapperMethod的execute方法,再此之前会创建MapperMethod实现两个内部类SqlCommand和MethodSignature的初始化。最后会根据配置执行相应的crud方法(具体的执行流程会在后面仔细讲解)