zoukankan      html  css  js  c++  java
  • MyBatis-parameterType 入参封装 Map 流程

    以如下入参为例,MyBatis 版本为 3.5.0

    public MyUser selectMyUserIdAndAge(Integer id, @Param("user") MyUser user);

    打上断点

    大致流程

    1、进入到 MapperProxy 类的 invoke 方法,执行接口的代理对象中的方法

    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 {
                // 代理以后,并不是所有 Mapper 的方法调用时都会调用这个 invoke 方法,如果这个方法是 Object 中通用的方法(toString、hashCode等)则无需执行
                if (Object.class.equals(method.getDeclaringClass())) {
                    return method.invoke(this, args);
                } else if (isDefaultMethod(method)) {
                    // 如果这个方法的权限修饰符是 public 并且是由接口提供,则执行 invokeDefaultMethod 方法,这里是由代理对象提供
                    return invokeDefaultMethod(proxy, method, args);
                }
            } catch (Throwable t) {
                throw ExceptionUtil.unwrapThrowable(t);
            }
            // 去缓存中找 MapperMethod
            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);
        }
    
        /**
         * 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();
        }
    }

    2、进入到 MapperMethod 类的 execute 方法,执行数据库操作

    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);
        }
    
        public Object execute(SqlSession sqlSession, Object[] args) {
            Object result;
            // 4种情况,insert|update|delete|select,分别调用 SqlSession 的 4 大类方法
            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()) {
                        // 如果结果是map
                        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;
        }
        ...
    }

    3、进入到 ParamNameResolver 类的 getNamedParams 方法,进行参数封装

    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();
            // 获取每个标了 @Param 注解的参数的值,赋值给 name
            final Annotation[][] paramAnnotations = method.getParameterAnnotations();
            // 记录参数索引与参数名称的关系
            final SortedMap<Integer, String> map = new TreeMap<>();
            int paramCount = paramAnnotations.length;
    
            // 遍历参数保存至 map:(key:参数索引,value:name的值)
            for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
                // 是否为特殊参数:RowBounds或ResultHandler
                if (isSpecialParameter(paramTypes[paramIndex])) {
                    continue;
                }
                String name = null;
                // 标注了 @Param 注解:name的值为注解的值
                for (Annotation annotation : paramAnnotations[paramIndex]) {
                    if (annotation instanceof Param) {
                        hasParamAnnotation = true;
                        name = ((Param) annotation).value();
                        break;
                    }
                }
                // 没有标 @Param 注解
                if (name == null) {
                    if (config.isUseActualParamName()) {
                        // 全局配置:useActualParamName(jdk1.8):name为参数名
                        name = getActualParamName(method, paramIndex);
                    }
                    if (name == null) {
                        // name为map.size():相当于当前元素的索引,0,1,2,3...
                        name = String.valueOf(map.size());
                    }
                }
                map.put(paramIndex, name);
            }
            names = Collections.unmodifiableSortedMap(map);
        }
    
        private String getActualParamName(Method method, int paramIndex) {
            return ParamNameUtil.getParamNames(method).get(paramIndex);
        }
    
        private static boolean isSpecialParameter(Class<?> clazz) {
            return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
        }
    
        public String[] getNames() {
            return names.values().toArray(new String[0]);
        }
    
        public Object getNamedParams(Object[] args) {
            final int paramCount = names.size();
            if (args == null || paramCount == 0) {
                // 没有参数
                return null;
            } else if (!hasParamAnnotation && paramCount == 1) {
                // 未使用@Param注解且参数列表只有一个,即 args[0] 参数的值
                return args[names.firstKey()];
            } else {
                // 为参数创建 param+ 索引的格式作为默认参数名称 如:param1  下标从1开始
                final Map<String, Object> param = new ParamMap<>();
                int i = 0;
                // 遍历names集合;{0=arg0, 1=user}
                for (Map.Entry<Integer, String> entry : names.entrySet()) {
                    // names集合的 value 作为 key;  names 集合的 key 作为取值的参考args[0]
                    // {arg0:args[0],user=args[1]}
                    param.put(entry.getValue(), args[entry.getKey()]);
                    final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
                    if (!names.containsValue(genericParamName)) {
                        // 额外的将每一个参数也保存到 param 中,使用新的key:param1...paramN,有 @Param 注解可以 #{指定的key},或者#{param1}
                        param.put(genericParamName, args[entry.getKey()]);
                    }
                    i++;
                }
                return param;
            }
        }
    }

    https://my.oschina.net/u/3737136/blog/1811654

    https://segmentfault.com/a/1190000015977801

    https://github.com/tuguangquan/mybatis/tree/master/src/main/java/org/apache/ibatis/binding

  • 相关阅读:
    Codeforces Round#410 Div.2
    AtCoder Beginner Contest-060
    如何将gedit变成c++编译器
    洛谷 P2486 [SDOI2011]染色
    让lu哥头痛了许久的代码(洛谷:树的统计)
    字符串模拟入门
    luogu P1553 数字反转(升级版)
    那些令人难忘的——坑
    luogu P1341 无序字母对
    最短路相关题目
  • 原文地址:https://www.cnblogs.com/jhxxb/p/10479583.html
Copyright © 2011-2022 走看看