Mybatis源码学习之反射工具(三)

简述

MyBatis在进行参数处理、结果映射等操作时,会涉及大量的反射操作。Java中的反射虽然功能强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码,MyBatis提供了专门的反射模块,该模块位于org.apache.ibatis.reflection包中,它对常见的反射操作做了进一步封装,提供了更加简洁方便的反射API。

Reflector & ReflectorFactory

Reflector

Reflector是MyBatis中反射模块的基础,每个Reflector对象都对应一个类,在Reflector中缓存了反射操作需要使用的类的元信息。Reflector 中各个字段的含义如下:


/**
 * 缓存了反射操作需要使用的类的元信息。
 * 允许在属性名和getter/setter方法之间轻松映射
 *
 * @author Clinton Begin
 */
public class Reflector {

    /**
     * 对应Class类型
     */
    private final Class<?> type;
    /**
     * 可读属性的名称集合,可读属性就是存在相应getter方法的属性,初始值为空数组
     */
    private final String[] readablePropertyNames;
    /**
     * 可写属性的名称集合,可写属性就是存在相应setter方法的属性,初始值为空数组
     */
    private final String[] writeablePropertyNames;
    /**
     * 记录了属性相应的setter方法,key是属性名称,value是Invoker对象,它是对setter方法对应Method对象的封装,
     */
    private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
    /**
     * 属性相应的getter方法集合,key是属性名称,value也是Invoker对象
     * <p>
     */
    private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
    /**
     * 记录了属性相应的setter方法的参数值类型,key是属性名称,value是setter方法的参数类型
     */
    private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
    /**
     * 记录了属性相应的getter方法的返回值类型,key是属性名称,value是getter方法的返回值类型
     */
    private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
    /**
     * 记录了默认构造方法
     */
    private Constructor<?> defaultConstructor;
    /**
     * 记录了所有属性名称集合
     */
    private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();

    /**
     * 构造函数,对上述字段初始化
     */
    public Reflector(Class<?> clazz) {
        type = clazz;
        addDefaultConstructor(clazz);
        addGetMethods(clazz);
        addSetMethods(clazz);
        addFields(clazz);
        readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
        writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
        for (String propName : readablePropertyNames) {
            caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
        for (String propName : writeablePropertyNames) {
            caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
    }

    /**
     * 获取指定Class对象的默认构造方法(包括无参构造方法)
     */
    private void addDefaultConstructor(Class<?> clazz) {
        Constructor<?>[] consts = clazz.getDeclaredConstructors();
        for (Constructor<?> constructor : consts) {
            //默认构造方法没有参数
            if (constructor.getParameterTypes().length == 0) {
                //允许利用反射检查任意类的私有变量
                if (canAccessPrivateMethods()) {
                    try {
                        constructor.setAccessible(true);
                    } catch (Exception e) {
                        // Ignored. This is only a final precaution, nothing we can do.
                    }
                }
                if (constructor.isAccessible()) {
                    this.defaultConstructor = constructor;
                }
            }
        }
    }

    /**
     * 负责解析类中定义的getter方法
     */
    private void addGetMethods(Class<?> cls) {
        Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
        Method[] methods = getClassMethods(cls);
        for (Method method : methods) {
            if (method.getParameterTypes().length > 0) {
                continue;
            }
            String name = method.getName();
            if ((name.startsWith("get") && name.length() > 3)
                    || (name.startsWith("is") && name.length() > 2)) {
                //获取字段名
                name = PropertyNamer.methodToProperty(name);
                addMethodConflict(conflictingGetters, name, method);
            }
        }
        resolveGetterConflicts(conflictingGetters);
    }

    private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
        //遍历conflictingGetters集合
        for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
            Method winner = null;
            //获取属性名
            String propName = entry.getKey();
            for (Method candidate : entry.getValue()) {
                if (winner == null) {
                    winner = candidate;
                    continue;
                }
                Class<?> winnerType = winner.getReturnType();
                Class<?> candidateType = candidate.getReturnType();
                if (candidateType.equals(winnerType)) {
                    if (!boolean.class.equals(candidateType)) {
                        throw new ReflectionException(
                                "Illegal overloaded getter method with ambiguous type for property "
                                        + propName + " in class " + winner.getDeclaringClass()
                                        + ". This breaks the JavaBeans specification and can cause unpredictable results.");
                    } else if (candidate.getName().startsWith("is")) {
                        winner = candidate;
                    }
                } else if (candidateType.isAssignableFrom(winnerType)) {
                    // OK getter type is descendant
                } else if (winnerType.isAssignableFrom(candidateType)) {
                    winner = candidate;
                } else {
                    //返回值相同,存在二义性,抛出异常
                    throw new ReflectionException(
                            "Illegal overloaded getter method with ambiguous type for property "
                                    + propName + " in class " + winner.getDeclaringClass()
                                    + ". This breaks the JavaBeans specification and can cause unpredictable results.");
                }
            }
            addGetMethod(propName, winner);
        }
    }

    /**
     * 收集get方法
     */
    private void addGetMethod(String name, Method method) {
        if (isValidPropertyName(name)) {
            getMethods.put(name, new MethodInvoker(method));
            Type returnType = TypeParameterResolver.resolveReturnType(method, type);
            getTypes.put(name, typeToClass(returnType));
        }
    }

    /**
     * 收集set方法
     */
    private void addSetMethods(Class<?> cls) {
        Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
        Method[] methods = getClassMethods(cls);
        for (Method method : methods) {
            String name = method.getName();
            if (name.startsWith("set") && name.length() > 3) {
                if (method.getParameterTypes().length == 1) {
                    name = PropertyNamer.methodToProperty(name);
                    addMethodConflict(conflictingSetters, name, method);
                }
            }
        }
        resolveSetterConflicts(conflictingSetters);
    }

    private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
        List<Method> list = conflictingMethods.get(name);
        if (list == null) {
            list = new ArrayList<Method>();
            conflictingMethods.put(name, list);
        }
        list.add(method);
    }

    private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
        for (String propName : conflictingSetters.keySet()) {
            List<Method> setters = conflictingSetters.get(propName);
            Class<?> getterType = getTypes.get(propName);
            Method match = null;
            ReflectionException exception = null;
            for (Method setter : setters) {
                Class<?> paramType = setter.getParameterTypes()[0];
                if (paramType.equals(getterType)) {
                    // should be the best match
                    match = setter;
                    break;
                }
                if (exception == null) {
                    try {
                        match = pickBetterSetter(match, setter, propName);
                    } catch (ReflectionException e) {
                        // there could still be the 'best match'
                        match = null;
                        exception = e;
                    }
                }
            }
            if (match == null) {
                throw exception;
            } else {
                addSetMethod(propName, match);
            }
        }
    }

    private Method pickBetterSetter(Method setter1, Method setter2, String property) {
        if (setter1 == null) {
            return setter2;
        }
        Class<?> paramType1 = setter1.getParameterTypes()[0];
        Class<?> paramType2 = setter2.getParameterTypes()[0];
        if (paramType1.isAssignableFrom(paramType2)) {
            return setter2;
        } else if (paramType2.isAssignableFrom(paramType1)) {
            return setter1;
        }
        throw new ReflectionException("Ambiguous setters defined for property '" + property + "' in class '"
                + setter2.getDeclaringClass() + "' with types '" + paramType1.getName() + "' and '"
                + paramType2.getName() + "'.");
    }

    private void addSetMethod(String name, Method method) {
        if (isValidPropertyName(name)) {
            setMethods.put(name, new MethodInvoker(method));
            Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
            setTypes.put(name, typeToClass(paramTypes[0]));
        }
    }

    private Class<?> typeToClass(Type src) {
        Class<?> result = null;
        if (src instanceof Class) {
            result = (Class<?>) src;
        } else if (src instanceof ParameterizedType) {
            result = (Class<?>) ((ParameterizedType) src).getRawType();
        } else if (src instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) src).getGenericComponentType();
            if (componentType instanceof Class) {
                result = Array.newInstance((Class<?>) componentType, 0).getClass();
            } else {
                Class<?> componentClass = typeToClass(componentType);
                result = Array.newInstance((Class<?>) componentClass, 0).getClass();
            }
        }
        if (result == null) {
            result = Object.class;
        }
        return result;
    }

    private void addFields(Class<?> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (canAccessPrivateMethods()) {
                try {
                    field.setAccessible(true);
                } catch (Exception e) {
                    // Ignored. This is only a final precaution, nothing we can do.
                }
            }
            if (field.isAccessible()) {
                if (!setMethods.containsKey(field.getName())) {
                    // issue #379 - removed the check for final because JDK 1.5 allows
                    // modification of final fields through reflection (JSR-133). (JGB)
                    // pr #16 - final static can only be set by the classloader
                    int modifiers = field.getModifiers();
                    if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
                        addSetField(field);
                    }
                }
                if (!getMethods.containsKey(field.getName())) {
                    addGetField(field);
                }
            }
        }
        if (clazz.getSuperclass() != null) {
            addFields(clazz.getSuperclass());
        }
    }

    private void addSetField(Field field) {
        if (isValidPropertyName(field.getName())) {
            setMethods.put(field.getName(), new SetFieldInvoker(field));
            Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
            setTypes.put(field.getName(), typeToClass(fieldType));
        }
    }

    private void addGetField(Field field) {
        if (isValidPropertyName(field.getName())) {
            getMethods.put(field.getName(), new GetFieldInvoker(field));
            Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
            getTypes.put(field.getName(), typeToClass(fieldType));
        }
    }

    private boolean isValidPropertyName(String name) {
        return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
    }

    /*
     * 此方法返回一个数组,该数组包含该类中声明的所有方法和任何超类
     * 我们使用此方法不是为了代替 Class.getMethods(),
     * 因为我们想访问类中的私有方法.
     *
     * @param cls Class对象
     * @return 包含该类中所有方法的数组
     */
    private Method[] getClassMethods(Class<?> cls) {
        //用于记录指定类中定义的全部方法的唯一签名以及对应的Method对象
        Map<String, Method> uniqueMethods = new HashMap<String, Method>();
        Class<?> currentClass = cls;
        while (currentClass != null) {
            //记录当前类中定义的所有方法
            addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());

            // 记录接口中定义的方法
            Class<?>[] interfaces = currentClass.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                addUniqueMethods(uniqueMethods, anInterface.getMethods());
            }
            //当前类的父类
            currentClass = currentClass.getSuperclass();
        }

        Collection<Method> methods = uniqueMethods.values();
        //转换成数组返回
        return methods.toArray(new Method[methods.size()]);
    }

    /**
     * 为每个方法生成唯一签名,并记录到uniqueMethods集合中
     */
    private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
        for (Method currentMethod : methods) {
            if (!currentMethod.isBridge()) {
                //得到方法签名
                String signature = getSignature(currentMethod);
                //根据方法签名排重
                if (!uniqueMethods.containsKey(signature)) {
                    if (canAccessPrivateMethods()) {
                        try {
                            currentMethod.setAccessible(true);
                        } catch (Exception e) {
                            // Ignored. This is only a final precaution, nothing we can do.
                        }
                    }
                    //记录签名与方法的对应关系
                    uniqueMethods.put(signature, currentMethod);
                }
            }
        }
    }

    /**
     * 获取方法签名,eg:java.lang.String#getSignature:java.lang.reflect.Method
     */
    private String getSignature(Method method) {
        StringBuilder sb = new StringBuilder();
        //方法返回类型
        Class<?> returnType = method.getReturnType();
        if (returnType != null) {
            sb.append(returnType.getName()).append('#');
        }
        sb.append(method.getName());
        Class<?>[] parameters = method.getParameterTypes();
        for (int i = 0; i < parameters.length; i++) {
            if (i == 0) {
                sb.append(':');
            } else {
                sb.append(',');
            }
            sb.append(parameters[i].getName());
        }
        return sb.toString();
    }

    /**
     * 获取访问私有方法的权限
     */
    private static boolean canAccessPrivateMethods() {
        try {
            SecurityManager securityManager = System.getSecurityManager();
            if (null != securityManager) {
                //允许利用反射检查任意类的私有变量
                securityManager.checkPermission(new ReflectPermission("suppressAccessChecks"));
            }
        } catch (SecurityException e) {
            return false;
        }
        return true;
    }

    /*
     * Gets the name of the class the instance provides information for
     *
     * @return The class name
     */
    public Class<?> getType() {
        return type;
    }

    public Constructor<?> getDefaultConstructor() {
        if (defaultConstructor != null) {
            return defaultConstructor;
        } else {
            throw new ReflectionException("There is no default constructor for " + type);
        }
    }

    public boolean hasDefaultConstructor() {
        return defaultConstructor != null;
    }

    public Invoker getSetInvoker(String propertyName) {
        Invoker method = setMethods.get(propertyName);
        if (method == null) {
            throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
        }
        return method;
    }

    public Invoker getGetInvoker(String propertyName) {
        Invoker method = getMethods.get(propertyName);
        if (method == null) {
            throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
        }
        return method;
    }

    /*
     * Gets the type for a property setter
     *
     * @param propertyName - the name of the property
     * @return The Class of the propery setter
     */
    public Class<?> getSetterType(String propertyName) {
        Class<?> clazz = setTypes.get(propertyName);
        if (clazz == null) {
            throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'");
        }
        return clazz;
    }

    /*
     * Gets the type for a property getter
     *
     * @param propertyName - the name of the property
     * @return The Class of the propery getter
     */
    public Class<?> getGetterType(String propertyName) {
        Class<?> clazz = getTypes.get(propertyName);
        if (clazz == null) {
            throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
        }
        return clazz;
    }

    /*
     * Gets an array of the readable properties for an object
     *
     * @return The array
     */
    public String[] getGetablePropertyNames() {
        return readablePropertyNames;
    }

    /*
     * Gets an array of the writeable properties for an object
     *
     * @return The array
     */
    public String[] getSetablePropertyNames() {
        return writeablePropertyNames;
    }

    /*
     * Check to see if a class has a writeable property by name
     *
     * @param propertyName - the name of the property to check
     * @return True if the object has a writeable property by the name
     */
    public boolean hasSetter(String propertyName) {
        return setMethods.keySet().contains(propertyName);
    }

    /*
     * Check to see if a class has a readable property by name
     *
     * @param propertyName - the name of the property to check
     * @return True if the object has a readable property by the name
     */
    public boolean hasGetter(String propertyName) {
        return getMethods.keySet().contains(propertyName);
    }

    public String findPropertyName(String name) {
        return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH));
    }
}

Reflector.addGetMethods()方法主要负责解析类中定义的getter方法,Reflector.addSetMethods()方法负责解析类中定义的setter方法,两者的逻辑类似,具体实现见源码。

ReflectorFactory接口

ReflectorFactory接口主要实现了对Reflector对象的创建和缓存,该接口定义如下:

public interface ReflectorFactory {

    /**
     * 是否会缓存Reflector对象
     */
    boolean isClassCacheEnabled();

    /**
     * 设置是否缓存Reflector对象
     */
    void setClassCacheEnabled(boolean classCacheEnabled);

    /**
     * 创建指定Class的Reflector对象
     */
    Reflector findForClass(Class<?> type);
}

MyBatis只为该接口提供了DefaultReflectorFactory这一个实现类,它与Reflector的关系如下:

image

DefaultReflectorFactory各字段及方法的含义如下:

public class DefaultReflectorFactory implements ReflectorFactory {
    /**
     * 默认开启对Reflector对象的缓存
     */
    private boolean classCacheEnabled = true;

    /**
     * 使用集合ConcurrentHashMap实现对Reflector的缓存
     */
    private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();

    public DefaultReflectorFactory() {
    }

    @Override
    public boolean isClassCacheEnabled() {
        return classCacheEnabled;
    }

    @Override
    public void setClassCacheEnabled(boolean classCacheEnabled) {
        this.classCacheEnabled = classCacheEnabled;
    }

    /**
     * 为指定的Class创建Reflector对象,并将Reflector对象缓存到reflectorMap中
     */
    @Override
    public Reflector findForClass(Class<?> type) {
        //检查是否开启缓存
        if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
            Reflector cached = reflectorMap.get(type);
            if (cached == null) {
                //创建Reflector对象
                cached = new Reflector(type);
                //放入ConcurrentHashMap集合缓存
                reflectorMap.put(type, cached);
            }
            return cached;
        } else {
            //没有开启缓存,直接创建Reflector对象并返回
            return new Reflector(type);
        }
    }

}

除了使用MyBatis提供的DefaultReflectorFactory实现,我们还可以在mybatis-config.xml中配置自定义的ReflectorFactory实现类,从而实现功能上的扩展。

TypeParameterResolver

TypeParameterResolver是一个工具类,提供了一系列静态方法来解析指定类中的字段、方法返回值或方法参数的类型。

在开始介绍TypeParameterResolver之前,先简单介绍一下Type接口的基础知识。Type是所有类型的父接口,它有四个子接口和一个实现类,

image

1、Class比较常见,它表示的是原始类型。

Class类的对象表示JVM中的一个类或接口,每个Java类在JVM里都表现为一个Class对象。在程序中可以通过“类名.class”、“对象.getClass()”或是“Class.forName(”类名”)”等方式获取Class对象。数组也被映射为Class 对象,所有元素类型相同且维数相同的数组都共享同一个 Class 对象。

2、ParameterizedType表示的是参数化类型

例如List<String>、Map<Integer,String>、Service<User>这种带有泛型的类型。

ParameterizedType接口中常用的方法有三个,分别是:

Type getRawType()—返回参数化类型中的原始类型,例如ListString>的原始类型为List。 

Type[] getActualTypeArguments()—获取参数化类型的类型变量或是实际类型列表,例如MapInteger, String>的实际泛型列表IntegerString。需要注意的是,该列表的元素类型都是Type,也就是说,可能存在多层嵌套的情况。 

Type getOwnerType()—返回是类型所属的类型,例如存在A<T>类,其中定义了内部类InnerA<I>,则InnerA<I>所属的类型为A<T>,如果是顶层类型则返回null。这种关系比较常见的示例是MapK,V>接口与Map.Entry<K,V>接口,MapK,V>接口是Map.Entry<K,V>接口的所有者。 · TypeVariable表示的是类型变量,它用来反映在JVM编译该泛型前的信息。例如List<T>中的T就是类型变量,它在编译时需被转换为一个具体的类型后才能正常使用。

TypeParameterResolver中各个静态方法之间的调用关系大致如图所示

image

TypeParameterResolver中通过resolveFieldType()方法、resolveReturnType()方法、resolveParamTypes()方法分别解析字段类型、方法返回值类型和方法参数列表中各个参数的类型。具体实现可查看其源码及官方的单元测试方法。

ObjectFactory接口

MyBatis中有很多模块会使用到ObjectFactory接口,该接口提供了多个create()方法的重载,通过这些create()方法可以创建指定类型的对象。ObjectFactory接口的定义如下:

/**
 * Mybatis 使用ObjectFactory去创建需要的对象
 * @author Clinton Begin
 */
public interface ObjectFactory {

    /**
     * 设置配置信息
     * @param properties 配置信息
     */
    void setProperties(Properties properties);

    /**
     * 通过默认构造函数创建指定类的对象
     * @param type 对象类型
     * @return
     */
    <T> T create(Class<T> type);

    /**
     * 根据参数列表,从指定类型中选择合适的构造器创建对象
     * @param type 对象类型
     * @param constructorArgTypes 参数类型
     * @param constructorArgs 参数值
     * @return
     */
    <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);

    /**
     * 检测指定类型是否是集合类型
     * @param type 对象类型
     * @return 集合类型返回true否则返回false
     * @since 3.1.0
     */
    <T> boolean isCollection(Class<T> type);

}

DefaultObjectFactory是MyBatis提供的ObjectFactory接口的唯一实现,它是一个反射工厂,其create()方法通过调用instantiateClass()方法实现。DefaultObjectFactory.instantiateClass()方法会根据传入传入的参数列表选择合适的构造函数实例化对象,具体实现如下:

private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
    try {
      Constructor<T> constructor;
      //通过无参构造函数创建对象
      if (constructorArgTypes == null || constructorArgs == null) {
        constructor = type.getDeclaredConstructor();
        if (!constructor.isAccessible()) {
          constructor.setAccessible(true);
        }
        return constructor.newInstance();
      }
      //根据参数列表查找合适的构造函数,并实例化对象
      constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()]));
      if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
      }
      return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()]));
    } catch (Exception e) {
      StringBuilder argTypes = new StringBuilder();
      if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) {
        for (Class<?> argType : constructorArgTypes) {
          argTypes.append(argType.getSimpleName());
          argTypes.append(",");
        }
        argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing ,
      }
      StringBuilder argValues = new StringBuilder();
      if (constructorArgs != null && !constructorArgs.isEmpty()) {
        for (Object argValue : constructorArgs) {
          argValues.append(String.valueOf(argValue));
          argValues.append(",");
        }
        argValues.deleteCharAt(argValues.length() - 1); // remove trailing ,
      }
      throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
    }
  }

除了使用MyBatis提供的DefaultObjectFactory实现,我们还可以在mybatis-config.xml配置文件中指定自定义的ObjectFactory接口实现类,从而实现功能上的扩展。

posted @ 2018-06-10 15:13  IT码客  阅读(294)  评论(0编辑  收藏  举报