MyBatis-反射模块

8/30/2021 MyBatis,源码

# MyBatis反射工具包

# 1.反射模块

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

# 1.1 Reflector

Reflector是反射模块的基础,每个Reflector对象都对应一个类,在Reflector中缓存了反射需要使用的类的元信息

# 1.1.1 属性

属性先关定义信息

// 对应的Class 类型 
private final Class<?> type;
// 可读属性的名称集合 可读属性就是存在 getter方法的属性,初始值为null
private final String[] readablePropertyNames;
// 可写属性的名称集合 可写属性就是存在 setter方法的属性,初始值为null
private final String[] writablePropertyNames;
// 记录了属性相应的setter方法,key是属性名称,value是Invoker方法
// 他是对setter方法对应Method对象的封装
private final Map<String, Invoker> setMethods = new HashMap<>();
// 属性相应的getter方法
private final Map<String, Invoker> getMethods = new HashMap<>();
// 记录了相应setter方法的参数类型,key是属性名称 value是setter方法的参数类型
private final Map<String, Class<?>> setTypes = new HashMap<>();
// 和上面的对应
private final Map<String, Class<?>> getTypes = new HashMap<>();
// 记录了默认的构造方法
private Constructor<?> defaultConstructor;

// 记录了所有属性名称的集合
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 1.1.2 构造方法

在Reflector的构造器中会完成相关的属性的初始化操作

// 解析指定的Class类型 并填充上述的集合信息
public Reflector(Class<?> clazz) {
    type = clazz; // 初始化 type字段
    addDefaultConstructor(clazz);// 设置默认的构造方法
    addGetMethods(clazz);// 获取getter方法
    addSetMethods(clazz); // 获取setter方法
    addFields(clazz); // 处理没有getter/setter方法的字段
    // 初始化 可读属性名称集合
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    // 初始化 可写属性名称集合
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    // caseInsensitivePropertyMap记录了所有的可读和可写属性的名称 也就是记录了所有的属性名称
    for (String propName : readablePropertyNames) {
        // 属性名称转大写
        caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
        // 属性名称转大写
        caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 1.1.2.1 添加默认构造方法

private void addDefaultConstructor(Class<?> clazz) {
    // 获取所有构造方法
    Constructor<?>[] constructors = clazz.getDeclaredConstructors();
    // 获取无参构造方法,并赋值给默认构造方法
    Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0)
        .findAny().ifPresent(constructor -> this.defaultConstructor = constructor);
}
1
2
3
4
5
6
7

# 1.1.2.2 添加Get方法

addGetMethods

private void addGetMethods(Class<?> clazz) {
    // 定义了一个存储getter方法的容器
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    // 获取Class及其父类中的所有的方法
    Method[] methods = getClassMethods(clazz);
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
        .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
    // 解决getter方法冲突的情况,其实就是重写的情况
    resolveGetterConflicts(conflictingGetters);
}
1
2
3
4
5
6
7
8
9
10

getClassMethods获取该class所有方法,包含父类方法

private Method[] getClassMethods(Class<?> clazz) {
    Map<String, Method> uniqueMethods = new HashMap<>();
    Class<?> currentClass = clazz;
    // 当前class不为空,并且当前不是Object -> 进入循环
    while (currentClass != null && currentClass != Object.class) {
        // 添加唯一方法
        addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());

        // we also need to look for interface methods -
        // because the class may be abstract
        Class<?>[] interfaces = currentClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            addUniqueMethods(uniqueMethods, anInterface.getMethods());
        }
        // 获取父类
        currentClass = currentClass.getSuperclass();
    }

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

// 添加唯一方法
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
        // 桥接方法的判断  泛型相关
        if (!currentMethod.isBridge()) {
            // 获取方法的唯一签名 返回类型#方法名称:参数类型列表
            // 获取唯一签名 [返回值类型#] + 方法名[:方法参数一, 方法参数二]
            String signature = getSignature(currentMethod);
            // check to see if the method is already known
            // if it is known, then an extended class must have
            // overridden a method
            // 不存在添加
            if (!uniqueMethods.containsKey(signature)) {
                uniqueMethods.put(signature, currentMethod);
            }
        }
    }
}


/**
   * 获取唯一签名 [返回值类型#] + 方法名[:方法参数一, 方法参数二]
   * @param method
   * @return
   */
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++) {
        sb.append(i == 0 ? ':' : ',').append(parameters[i].getName());
    }
    return sb.toString();
}


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

resolveGetterConflicts 解决getter方法冲突的情况,其实就是重写的情况,并且保存在Map中,value值已封装成Invoker对象

# 1.1.2.3 添加Set方法

和弹添加Get方法一样,巴拉巴拉

# 1.1.2.4 添加字段方法

处理没有getter/setter方法的字段

有些字段未提供get、set方法,addFilelds()方法主要添加未提供get、set方法的属性字段

反射我们也可以在项目中我们直接拿来使用,定义一个普通的Bean对象。

public static void main(String[] args) {
    Reflector reflector = new Reflector(Student.class);
}
1
2
3

# 1.1.3 公共的API方法

方法名称 作用
getType 获取Reflector表示的Class
getDefaultConstructor 获取默认的构造器
hasDefaultConstructor 判断是否有默认的构造器
getSetInvoker 根据属性名称获取对应的Invoker 对象
getGetInvoker 根据属性名称获取对应的Invoker对象
getSetterType 获取属性对应的类型 比如:
String name; // getSetterType("name") --> java.lang.String
getGetterType 与上面是对应的
getGetablePropertyNames 获取所有的可读属性名称的集合
getSetablePropertyNames 获取所有的可写属性名称的集合
hasSetter 判断是否具有某个可写的属性
hasGetter 判断是否具有某个可读的属性
findPropertyName 根据名称查找属性

在MyBatis中给我们提供了一个ReflectorFactory工厂对象。所以我们先来简单了解下ReflectorFactory对象,当然你也可以直接new 出来,像上面的案例一样

# 1.2 ReflectorFactory

ReflectorFactory接口主要实现了对Reflector对象的创建和缓存

# 1.2.1 ReflectorFactory接口的定义

public interface ReflectorFactory {
    // 检测该ReflectorFactory是否缓存了Reflector对象
    boolean isClassCacheEnabled();
    // 设置是否缓存Reflector对象
    void setClassCacheEnabled(boolean classCacheEnabled);
    // 创建指定了Class的Reflector对象
    Reflector findForClass(Class<?> type);
}
1
2
3
4
5
6
7
8

然后我们来看看它的具体实现

# 1.2.2 DefaultReflectorFactory

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

DefaultReflectorFactory中的实现

public class DefaultReflectorFactory implements ReflectorFactory {
    private boolean classCacheEnabled = true;
    // 实现对 Reflector 对象的缓存
    private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();

    public DefaultReflectorFactory() {
    }

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

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

    @Override
    public Reflector findForClass(Class<?> type) {
        if (classCacheEnabled) {// 开启缓存
            // synchronized (type) removed see issue #461
            return reflectorMap.computeIfAbsent(type, Reflector::new);
        } else {
            // 没有开启缓存就直接创建
            return new Reflector(type);
        }
    }
}
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

# 1.2.3 使用演示

public static void main(String[] args) {
    DefaultReflectorFactory factory = new DefaultReflectorFactory();
    Reflector reflector = factory.findForClass(Student.class);
}
1
2
3
4

# 1.3 Invoker

针对于Class中Field和Method的调用,在MyBatis中封装了Invoker对象来统一处理

/**
 * @author Clinton Begin
 */
public interface Invoker {
    // 执行Field或者Method
    Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;

    // 返回属性相应的类型
    Class<?> getType();
}
1
2
3
4
5
6
7
8
9
10

该接口有对应的三个实现

# 1.3.2 效果演示

public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException {
    DefaultReflectorFactory factory = new DefaultReflectorFactory();
    Reflector reflector = factory.findForClass(Student.class);
    Object o = reflector.getDefaultConstructor().newInstance();
    Invoker numSetInvoker = reflector.getSetInvoker("num");
    Invoker numGetInvoker = reflector.getGetInvoker("num");
    // 设置值
    numSetInvoker.invoke(o,new Object[]{11});
    // 获取值
    Object res = numGetInvoker.invoke(o, null);
    System.out.println(res);
}
1
2
3
4
5
6
7
8
9
10
11
12
Connected to the target VM, address: '127.0.0.1:5024', transport: 'socket'
11
Disconnected from the target VM, address: '127.0.0.1:5024', transport: 'socket'

Process finished with exit code 0
1
2
3
4
5

# 1.4 MetaClass

在Reflector中可以针对普通的属性操作,但是如果出现了比较复杂的属性,比如 private Person person; 这种,我们要查找的表达式 person.userName.针对这种表达式的处理,这时就可以通过MetaClass来处理了。我们来看看主要的属性和构造方法

/**
 * 通过 Reflector 和 ReflectorFactory 的组合使用 实现对复杂的属性表达式的解析
 * @author Clinton Begin
 */
public class MetaClass {
    // 缓存 Reflector
    private final ReflectorFactory reflectorFactory;
    // 创建 MetaClass时 会指定一个Class reflector会记录该类的相关信息
    private final Reflector reflector;

    private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
        this.reflectorFactory = reflectorFactory;
        this.reflector = reflectorFactory.findForClass(type);
    }
    // ....
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

测试代码

public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException {
    MetaClass metaClass = MetaClass.forClass(User.class, new DefaultReflectorFactory());
    System.out.println(metaClass.hasGetter("student.cat.color"));
    System.out.println(metaClass.findProperty("student.cat.color"));
}
1
2
3
4
5

输出结果

Connected to the target VM, address: '127.0.0.1:4086', transport: 'socket'
true
student.cat.color
Disconnected from the target VM, address: '127.0.0.1:4086', transport: 'socket'
1
2
3
4

# 1.5 MetaObject

我们可以通过MetaObject对象解析复杂的表达式来对提供的对象进行操作。具体的通过案例来演示会更直观些

@Test
public void shouldGetAndSetField() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richField", "foo");
    System.out.println(meta.getValue("richField"));
}

@Test
public void shouldGetAndSetNestedField() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richType.richField", "foo");
    System.out.println(meta.getValue("richType.richField"));
}

@Test
public void shouldGetAndSetMapPairUsingArraySyntax() {
    RichType rich = new RichType();
    MetaObject meta = SystemMetaObject.forObject(rich);
    meta.setValue("richMap[key]", "foo");
    System.out.println(meta.getValue("richMap[key]"));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 1.6 反射模块应用

# SqlSessionFactory

在创建SqlSessionFactory操作的时候会完成Configuration对象的创建,而在Configuration中默认定义的ReflectorFactory的实现就是DefaultReflectorFactory对象

然后在解析全局配置文件的代码中,给用户提供了ReflectorFactory的扩展,也就是我们在全局配置文件中可以通过

reflectorFactory标签来使用我们自定义的ReflectorFactory

# 1.6.2 SqlSession

无相关操作

# 1.6.3 Mapper

无相关操作

# 1.6.4 执行SQL

在Statement获取结果集后,在做结果集映射的使用有使用到,在DefaultResultSetHandler的createResultObject方法中。

有序MyBatis框架中大量使用了反射,为了简化相关操作,简化操作才定义了反射模块,用到的地方还是挺多的,大家自行观看!巴拉巴拉~~~

Last Updated: 9/6/2022, 3:13:28 PM