我正在参加「掘金·启航计划」 「反射基本 API 使用」juejin.cn/post/720144…
「反射实战:从 cotroller 类找到 xml 文件」juejin.cn/post/724667…
以前写过基本的反射使用,并且实操过。虽然只是小试牛刀,但已经感受到了反射的强大与魅力。MyBatis 是 Java 写的框架,自然少不了反射的大量运用,为了降低反射使用成本,MyBatis 封装了反射工具箱:位于 org.apache.ibatis.reflection 包中。下面是 MyBatis-3.5.10 的源码:
Reflector
Reflector 是 MyBatis 放射模块的基础,要使用反射模块操作一个 Class 都会将其先封装为一个 Reflector 对象。它会缓存反射操作需要的类的信息,比如:构造方法/属性名/get、set方法等。
初始化构造函数
addDefaultConstructor 会将传入的 class 的空构造函数缓存,初始化 defaultConstructor 字段
获得当前 class 所有方法
getClassMethods 获得 class 及其父类/接口(public)的方法缓存,其中处理的时候会用到桥接函数,判断不是桥接函数才会加入。
桥接方法
桥接方法:当一个子类在继承或者实现一个父类或接口的泛型方法时,在子类中明确指定了泛型类型,那么在编译时编译器会自动生成桥接方法。
什么是桥接?下面有两个类,AbstractEntity 实现了 Entity 的泛型方法,在字节码中 AbstractEntity 会生成四个方法,其中两个方法是编译器自动生成的与父类方法签名一致的桥接方法。下面的就是获取到 AbstractEntity 生成的方法,其中两个是和父类一致的桥接方法
interface Entity<T> {
T getId();
void setId(T id);
}
static abstract class AbstractEntity implements Entity<Long> {
private Long id;
@Override
public Long getId() {
return id;
}
@Override
public void setId(Long id) {
this.id = id;
}
}
获取所有的 get/set 方法
addGetMethods/addSetMethods
都是先遍历出 get/set 相关的方法,像上面的方法,有泛型方法和具体的实现类,那么这里加入的时候会判断 isAssignableFrom「一个类是另一个类的子类或者相同类,那么前者就可以被赋值给后者」。
最后再加入 getMethods(基于 Method 的 Invoker类)、getTypes(方法类型)/setMethods、setTypes
加入属性
addFields 会遍历当前的属性以及父类的属性字段并加入(当然不会有实现的接口)
赋值可读/写属性数组
readablePropertyNames = getMethods.keySet().toArray(new String[0]);
writablePropertyNames = setMethods.keySet().toArray(new String[0]);
最后将所有属性转为大写保存到 caseInsensitivePropertyMap 属性中。
public Reflector(Class<?> clazz) {
type = clazz;
addDefaultConstructor(clazz);
Method[] classMethods = getClassMethods(clazz);
if (isRecord(type)) {
addRecordGetMethods(classMethods);
} else {
addGetMethods(classMethods);
addSetMethods(classMethods);
addFields(clazz);
}
readablePropertyNames = getMethods.keySet().toArray(new String[0]);
writablePropertyNames = setMethods.keySet().toArray(new String[0]);
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
ReflectorFactory
Reflector 工厂接口,用于创建和缓存 Reflector 对象
DefaultReflectorFactory
Reflector 工厂接口的具体实现类,其中 findClass 最重要
如果允许缓存就从缓存里面拿,没有则 new
不允许缓存就每次都 new
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
return MapUtil.computeIfAbsent(reflectorMap, type, Reflector::new);
} else {
return new Reflector(type);
}
}
Invoker
在 Reflector 对象的初始化过程中,所有属性的 getter/setter 方法都会被封装成 MethodInvoker 对象,没有 getter/setter 的字段也会生成对应的 Get/SetFieldInvoker 对象。
默认对象工厂
ObjectFactory 是 MyBatis 中的反射工厂,其中提供了两个 create() 方法的重载,我们可以通过两个 create() 方法创建指定类型的对象。
其中 DefaultObjectFactory 是默认实现,我们还可以在 mybatis-config.xml 中配置自定义 ObjectFactory 接口扩展实现类。
属性解析
在 refelection/property 包下的三个类:
PropertyTokenizer
负责解析由 “.”和“[]” 构成的表达式,因为继承了 Iterator 接口, 可以处理多层嵌套
PropertyCopier
属性拷贝的工具类,提供了 Spring 中 BeanUtils.copyProperties() 类似的功能,实现相同类型的两个对象之间的属性值拷贝。
PropertyNamer
转换方法名到属性名,以及检测一个方法是否为 getter / setter 方法。
MetaClass
MetaClass 只有下面两个属性,依赖 Reflector。它提供获取类中属性描述信息的功能。且其中也大量依赖了 PropertyTokenizer 解析表达式。
private final ReflectorFactory reflectorFactory;
private final Reflector reflector;
ObjectWrapper
MetaClass 中封装的是 Class 元信息,ObjectWrapper 封装的则是对象元信息。在 ObjectWrapper 中抽象了一个对象的属性信息,并提供了查询对象属性信息的相关方法,以及更新属性值的相关方法。
- BaseWrapper 是抽象类,其其中方法都是针对集合对象的处理,其中都是依赖 PropertyTokenizer 解析类。
- CollectionWrapper 是 ObjectWrapper 接口针对 Collection 集合的一个实现,其中封装了Collection 集合对象,只有 isCollection()、add()、addAll() 方法以及从 BaseWrapper 继承下来的方法是可用的,其他方法都会抛出 UnsupportedOperationException 异常
- BeanWrapper 继承了 BaseWrapper 抽象类,底层除了封装了一个 JavaBean 对象之外,还封装了该 JavaBean 类型对应的 MetaClass 对象,以及从 BaseWrapper 继承下来的 MetaObject 对象
- MapWrapper 继承了 BaseWrapper ,里面只有一个属性 Map<String, Object> 其中的相关操作都是 get/put
总结
总的来说学下来感觉代码量有点大,虽然以前学过基本的反射 API ,但真的就是基本的,比如 判断桥接方法和 isAssignableFrom 方法都是先学,就感觉有点吃力。到现在也是有点点懵。
上面介绍了反射工具箱中最核心、最底层的 Reflector 类的核心实现;接下来介绍了反射工具箱在 Reflector 基础之上提供的各种工具类,其中包括 ReflectorFactory 工厂类、ObjectFactory 工厂类、ObjectWrapper 包装类以及记录元数据的 MetaClass 等。
还需继续努力。