MyBatis在进行参数处理、结果映射等操作的时候,会涉及大量的反射操作。java原生反射操作有些复杂且容易出错,MyBatis中提供了反射相关的工具类放在com.apache.ibatis.reflection包中。同时MyBatis中提供的测试用例也是非常值得一看的。
1.Reflector&ReflectorFactory
对JavaBean的定义:属性是通过getter/setter方法得到的,也就是说有属性A就会有getA(),setA(A)方法。
Reflector是反射的基础,里面缓存了反射操作需要使用的类的元信息。以下为各个字段的含义
public class Reflector {
// 对应class的类型
private final Class<?> type;
// 可读属性的集合(存在相应getter方法的属性)
private final String[] readablePropertyNames;
// 可写属性的集合(存在相应setter方法的属性)
private final String[] writablePropertyNames;
// 记录属性对应的setter方法
private final Map<String, Invoker> setMethods = new HashMap<>();
// 记录属性对应的getter方法
private final Map<String, Invoker> getMethods = new HashMap<>();
// 记录setter方法的参数类型
private final Map<String, Class<?>> setTypes = new HashMap<>();
// 记录getter方法返回值的类型
private final Map<String, Class<?>> getTypes = new HashMap<>();
// 记录默认构造方法
private Constructor<?> defaultConstructor;
// 记录所有属性名称的集合 key为大写的属性,包含父类的属性
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
// 构造器主要用于初始化并填充上面的属性
public Reflector(Class<?> clazz) {
type = clazz;
addDefaultConstructor(clazz);
// 填充Getter方法
addGetMethods(clazz);
// 填充Setter方法
addSetMethods(clazz);
// 处理没有Getter/Setter方法的字段
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);
}
}
构造器中有一个重要的方法是addGetMethods(clazz);用于获取属性的getter方法。
private void addGetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingGetters = new HashMap<>();
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));
resolveGetterConflicts(conflictingGetters);
}
主要包含有3个步骤
1.getClassMethods(clazz);获取当前类所有的方法(包括父类)
private Method[] getClassMethods(Class<?> clazz) {
// 记录指定类中定义的全部方法和唯一签名以及对应的Method对象
Map<String, Method> uniqueMethods = new HashMap<>();
Class<?> currentClass = clazz;
while (currentClass != null && currentClass != Object.class) {
// 记录当前类的全部方法
addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
Class<?>[] interfaces = currentClass.getInterfaces();
for (Class<?> anInterface : interfaces) {
addUniqueMethods(uniqueMethods, anInterface.getMethods());
}
// 获取父类继续循环
currentClass = currentClass.getSuperclass();
}
Collection<Method> methods = uniqueMethods.values();
// 转换成Methods数组返回
return methods.toArray(new Method[0]);
}
addUniqueMethods()方法会为每个方法生成唯一签名,并记录到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)) {
uniqueMethods.put(signature, currentMethod);
}
}
}
}
2.然后从getClassMethos()方法返回的Method数组中查找该类中定义的getter方法,将其记录到conflictingGetters集合中,key为属性名称,value是该属性对应的getter方法集合。
3.当子类覆盖了父类的getter方法且返回值发生变化时,在步骤1中就会产生两个不同的方法,在java中这种操作是合法的但这又不是我们想要的,所以第3要使用addGetMethods(clazz);方法对这种覆盖的情况进行处理,同时会将处理得到的Getter方法记录到getMethods集合,并将其返回值类型填充到getTypes集合中。
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null;
String propName = entry.getKey();
boolean isAmbiguous = false;
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)) {
isAmbiguous = true;
break;
} 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 {
isAmbiguous = true;
break;
}
}
addGetMethod(propName, winner, isAmbiguous);
}
}
Reflector中提供了多个get*()方法用于读取记录的元信息。值得注意的是,在add*Method()获取Method时会统一封装为Invoker对象。
public interface Invoker {
// 调用获取指定字段的值或执行指定的方法
Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException;
// 属性的类型
Class<?> getType();
}
Invoker的实现类有SetFieldInvoker、GetFieldInvoker、MethodInvoker。前两者通过field字段封装了对应的Field对象,两者的invoke()方法通过调用Field.get()/set()方法实现的。MethodInvoker通过代哦用Method.invoker()方法实现。
ReflectionFactory接口主要实现对Reflection对象的创建和缓存,该接口定义:
/**
* 主要实现对Reflection对象的创建和缓存
*
* @author Clinton Begin
*/
public interface ReflectorFactory {
/**
* 监测ReflectorFactory对象是否会缓存
*
* @return
*/
boolean isClassCacheEnabled();
/**
* 设置是否缓存ReflectorFactory对象
*
* @param classCacheEnabled
*/
void setClassCacheEnabled(boolean classCacheEnabled);
/**
* 创建指定class对应的Reflector对象
*
* @param type
* @return
*/
Reflector findForClass(Class<?> type);
ReflectionFactory对象只有一个实现类DefaultReflectorFactory
2.TypeParameterResolver
它提供了一系列方法来解析指定类中的字段、方法返回值、或方法参数的类型。
首先复习一下java中的Type,Type接口源于原生JDK,它有4个子接口(ParameterizedType、GenericArrayType、TypeVariable、WildcardType)和1个实现类(Class)。
- Class:它表示原始类型,Class类对象表示JVM中的一个类或接口,每个java类在JVM里都表现为一个Class对象
- ParamterizedType:表示的是参数化类型,例如List、Map<Integer, String>、Service这种带有泛型的类型。
Type getRawType(): 返回参数化类型中的原始类型,例如List的原始类型为List
Type[] getActualArguments():获取参数化类型变量或实际类型列表,例如Map<Integer,String>的实际泛型列表是Integer、String
Type getOwnerType():返回类型所属的类型,例如存在A类,其中定义了内部InnerA,则Inner<A,I>所属的类型为A,如果没顶层类型则返回为null - TypeVariable:表示的是类型变量,它用来反映在JVM编译该泛型前的信息。例如List中的T就是类型变量,它在编译时需要每转换为一个具体的类型后才能正常使用。
Type[] getBounds():获取类型变量的上边界,如果未明确声明上边界则默认为Object,例如class Test中K的上界就是Person。
D getGenericDeclaration():获取类型变量的原始类型 Test的原始类型就是Test
String getName():获取在源码中定义的名字,上例中为K
GenericArrayType() 表示的是数组类型且组成元素是ParameterizedType或者TypeVariable,例如List,该接口只有Type getGenericComponentType()一个方法,它返回数组的组成元素。 - WildcardType表示的是通配符泛型,例如: ?extends Person
Type[] getUpperBounds()返回泛型变量的上界
Type[] getLowerBounds()返回泛型变量的下界
TypeParameterResolver中通过resolverFieldType()方法、resolverReturnType()、resolverParamType()方法分别解析字段类型、方法返回类型、方法参数类型。这三个方法都有用到resolveType()方法。在MyBatis此类的测试用例也非常丰富,可以看测试用例了解这个类的使用
3.ObjectFactory
该接口提供了多个create()方法的重载,通过这些create()方法可以创建指定类型的对象。
/**
* MyBatis uses an ObjectFactory to create all needed new Objects.
* 使用create创建指定类型的对象
*
* @author Clinton Begin
*/
public interface ObjectFactory {
/**
* 设置配置行
*
* @param properties configuration properties
*/
default void setProperties(Properties properties) {
}
/**
* 通过无参构造器创建指定的类对象
*
* @param <T> the generic type
* @param type Object type
* @return the t
*/
<T> T create(Class<T> type);
/**
* 根据参数列表选择合适的构造器创建对象
*
* @param <T> the generic type
* @param type Object type
* @param constructorArgTypes Constructor argument types
* @param constructorArgs Constructor argument values
* @return the t
*/
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
/**
* 监测指定类型是否为集合
*
* @param <T> the generic type
* @param type Object type
* @return whether it is a collection or not
* @since 3.1.0
*/
<T> boolean isCollection(Class<T> type);
}
DefaultObjectFacroty是MyBatis提供的ObjectFactory接口的唯一实现,它是一个反射工厂,其create()方法通过调用instantiateClass()方法实现。
4.Property工具集
包含三个属性工具:PropertyTokenizer、PropertyName和PropertyCopier。
PropertyTokenizer:解析类似“orders[0].items[0].name”的表达式,它可以迭代处理嵌套多层表达式。next()方法 就会创建一个新的PropertyTokenizer对象。
PropertyNamer是另一个工具类。
public final class PropertyNamer {
private PropertyNamer() {
}
/**
* 将getter、setter方法名转为属性名(去掉前缀is、get、set)
*
* @param name
* @return
*/
public static String methodToProperty(String name) {
if (name.startsWith("is")) {
name = name.substring(2);
} else if (name.startsWith("get") || name.startsWith("set")) {
name = name.substring(3);
} else {
throw new ReflectionException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'.");
}
if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
return name;
}
/**
* 检测方法名是否对应属性名
*
* @param name
* @return
*/
public static boolean isProperty(String name) {
return isGetter(name) || isSetter(name);
}
/**
* 检查是否为getter方法
*
* @param name
* @return
*/
public static boolean isGetter(String name) {
return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
}
/**
* 检查是否为setter方法
*
* @param name
* @return
*/
public static boolean isSetter(String name) {
return name.startsWith("set") && name.length() > 3;
}
}
PropertyCopier是一个属性拷贝工具
public final class PropertyCopier {
private PropertyCopier() {
// Prevent Instantiation of Static Class
}
public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
Class<?> parent = type;
while (parent != null) {
final Field[] fields = parent.getDeclaredFields();
for (Field field : fields) {
try {
try {
field.set(destinationBean, field.get(sourceBean));
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
field.set(destinationBean, field.get(sourceBean));
} else {
throw e;
}
}
} catch (Exception e) {
// Nothing useful to do, will only fail on final fields, which will be ignored.
}
}
parent = parent.getSuperclass();
}
}
5.MetaClass
通过Reflector和PropertyTokenizer组合使用,可以解析复杂的属性表达式。是对类级别元信息的封装
public class MetaClass {
/**
* 主要用于缓存Reflector对象
*/
private final ReflectorFactory reflectorFactory;
/**
* 创建MetaClass时会指定该类,该对象记录了该类相关的元信息
*/
private final Reflector reflector;
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
return new MetaClass(type, reflectorFactory);
}
}
MetaClass可以获取类的元信息
@Test
void shouldTestDataTypeOfGenericMethod() {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
MetaClass meta = MetaClass.forClass(GenericConcrete.class, reflectorFactory);
// 根据属性名字,获取属性的类型
assertEquals(Long.class, meta.getGetterType("id"));
assertEquals(Long.class, meta.getSetterType("id"));
}
findProperty()方法可以查看到是否有某属性
@Test
void shouldCheckGetterExistance() {
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
MetaClass meta = MetaClass.forClass(RichType.class, reflectorFactory);
assertTrue(meta.hasGetter("richField"));
assertEquals("richType.richProperty", meta.findProperty("richType.richProperty", false));
}
hasSetter()、hasGetter判断是否有对应的属性
/**
* 判断是否有对应的属性,最终会调用Reflector.addFields()方法,当字段没有对应的getter/setter会自动添加
*
* @param name
* @return
*/
public boolean hasGetter(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
// 判断指定的属性是否有getter方法
if (reflector.hasGetter(prop.getName())) {
MetaClass metaProp = metaClassForProperty(prop);
return metaProp.hasGetter(prop.getChildren());
} else {
return false;
}
} else {
return reflector.hasGetter(prop.getName());
}
}
6.ObjectWrapper
/**
* 对对象的封装
*
* @author Clinton Begin
*/
public interface ObjectWrapper {
/**
* 普通对像,就会调用getter方法。如果是集合类则获取指定key或者下标对应的value
*
* @param prop
* @return
*/
Object get(PropertyTokenizer prop);
/**
* 普通bean调用setter方法,集合类设置指定key或下标对应的value值
*
* @param prop
* @param value
*/
void set(PropertyTokenizer prop, Object value);
/**
* 查找属性表达式指定的属性
*
* @param name
* @param useCamelCaseMapping 是否忽略表达式中的下划线
* @return
*/
String findProperty(String name, boolean useCamelCaseMapping);
/**
* 查找可写属性的名称集合
*
* @return
*/
String[] getGetterNames();
/**
* 查找可读属性的名称集合
*
* @return
*/
String[] getSetterNames();
/**
* 获取属性表达式属性的setter方法的参数类型
*
* @param name
* @return
*/
Class<?> getSetterType(String name);
/**
* 属性表达式对应属性的getter方法返回值的类型
*
* @param name
* @return
*/
Class<?> getGetterType(String name);
/**
* 是否有setter方法
*
* @param name
* @return
*/
boolean hasSetter(String name);
/**
* 是否有getter方法
*
* @param name
* @return
*/
boolean hasGetter(String name);
/**
* 为属性表达式指定的属性创建相应的MetaClass对象
*
* @param name
* @param prop
* @param objectFactory
* @return
*/
MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
/**
* 封装的对象是否为集合
*
* @return
*/
boolean isCollection();
/**
* 调用集合中的add()方法
*
* @param element
*/
void add(Object element);
/**
* 调用集合中的addAll()
*
* @param element
* @param <E>
*/
<E> void addAll(List<E> element);
}
BaseWrapper实现类ObjectWrapper接口的抽象类,其中setCollectionValue()和getCollectonValue().方法会解析属性表达式的索引信息,然后获取/设置对应项
BeanWrapper继承了Basewrapper
7.MetaObject
public class MetaObject {
/**
* 原始JavaBean对象
*/
private final Object originalObject;
private final ObjectWrapper objectWrapper;
private final ObjectFactory objectFactory;
private final ObjectWrapperFactory objectWrapperFactory;
private final ReflectorFactory reflectorFactory;
......
}
构造方法会根据传入的原始对象的类型以及ObjectFactory工厂的实现,创建相应的ObjectWrapper对象。