Mybatis源码解析一——搭建阅读环境
Mybatis源码解析二——解析器模块(parsing)
Mybatis源码解析三——反射模块(Reflector)
public interface ReflectorFactory {
/**
* 是否开启Reflector类缓存
* @return
*/
boolean isClassCacheEnabled();
/**
* 设置是否开启Reflector类缓存
* @param classCacheEnabled
*/
void setClassCacheEnabled(boolean classCacheEnabled);
/**
* 获取指定类的 Reflector
* @param type
* @return
*/
Reflector findForClass(Class<?> type);
}
ReflectorFactory工厂,顾名思义,是用来创建Reflector对象的,定义了三个接口,很简单。
可以看到这个接口只有一个实现类DefaultReflectorFactory
public class DefaultReflectorFactory implements ReflectorFactory {
private boolean classCacheEnabled = true;
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);
}
}
}
可以看到,默认情况下,DefaultReflectorFactory是开启缓存的。最重要的是 findForClass 方法,开启缓存,就先查询缓存,没有则创建。关闭缓存,则每次调用都创建新的Reflector对象。
下面就看看Reflector这个核心对象。
稍微会有些复杂,一点点来看,首先我们看看对象内部的属性有哪些
public class Reflector {
/**
* 对应的类
*/
private final Class<?> type;
/**
* 可读属性数组
*/
private final String[] readablePropertyNames;
/**
* 可写属性数组
*/
private final String[] writablePropertyNames;
/**
* setting方法映射
*
* key: 属性名
* value: 方法
*/
private final Map<String, Invoker> setMethods = new HashMap<>();
/**
* getting方法映射
*/
private final Map<String, Invoker> getMethods = new HashMap<>();
/**
* setting方法参数映射
*/
private final Map<String, Class<?>> setTypes = new HashMap<>();
/**
* getting方法返回数据类型映射
*/
private final Map<String, Class<?>> getTypes = new HashMap<>();
/**
* 默认构造方法
*/
private Constructor<?> defaultConstructor;
/**
* 忽略大小写的属性集合
*/
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
}
可以看出,Reflector对象目的就是把一个类的属性和其方法对应起来,组成map集合,以方便调用。
在Reflector内部,提供了一个有参构造,这个构造器非常重要,并且代码量比较大。
public class Reflector {
public Reflector(Class<?> clazz) {
type = clazz;
//设置默认构造器,就是获取无参构造
addDefaultConstructor(clazz);
//设置getting方法
addGetMethods(clazz);
//设置setting方法
addSetMethods(clazz);
//设置属性 有一些属性没有提供get或者set方法,所以在get、set方法设定的时候,会漏掉这些属性
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);
}
}
}
在构造器中,调用了一系列方法,完成了对上述的几个属性的初始化,底层调用的是java的反射相关方法,当然有MyBatis自己的处理,一个个来看。
这个方法的作用是获取默认的构造器
private void addDefaultConstructor(Class<?> clazz) {
//获取当前clazz中声明的构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
//流式便利,过滤出参数列表长度为0的构造器,也就是无参构造,如果找到,就赋值给defaultConstructor属性
Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0)
.findAny().ifPresent(constructor -> this.defaultConstructor = constructor);
}
顾名思义,获得所有的get方法
private void addGetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingGetters = new HashMap<>();
//获取类的方法
Method[] methods = getClassMethods(clazz);
//流式便利方法,过滤出无参、且方法名为“get”或“is”开头 的方法,也就是getting方法
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
// PropertyNamer.methodToProperty(m.getName()) 根据方法名获取属性名
//将getting方法放入映射,key为属性名,value为get方法集合
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
//解决getting方法冲突 最终一个属性只能保留一个对应的方法
resolveGetterConflicts(conflictingGetters);
}
getClassMethods
private Method[] getClassMethods(Class<?> clazz) {
//每个方法的签名与该方法的映射
Map<String, Method> uniqueMethods = new HashMap<>();
Class<?> currentClass = clazz;
//循环类,类的父类,父类的父类,直到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();
return methods.toArray(new Method[0]);
}
这里是从子类到父类的一个循环,一直到Object类。
addUniqueMethods
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
//如果当前方法签名不存在 uniqueMethods 中,就put
if (!uniqueMethods.containsKey(signature)) {
uniqueMethods.put(signature, currentMethod);
}
}
}
}
判断是否是桥接方法,是因为java的泛型是伪泛型,不清楚的可以看看java泛型擦除相关的知识。
至此,获得了当前类的所有方法,然后使用流式便利筛选出get方法,isGetter方法很简单,这里不贴了。
methodToProperty方法的作用就是根据方法名获取到属性名,很简单,略过。
addMethodConflict方法是生成Map
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
//判读是否是有效的属性名
if (isValidPropertyName(name)) {
//获取key为当前属性名的方法集合,如果不存在或者为null,就返回一个新的集合
//JDK8的写法,针不戳
List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
//将当前方法加入这个集合
list.add(method);
}
}
conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>()); java8的写法,还是很舒服的!
resolveGetterConflicts方法主要是解决冲突,因为之前得到的map中,属性key对应的get方法可能有多个,这里要选出最合适的get方法。
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
//遍历获取到的Map集合
//遍历每个属性,查找其最匹配的方法。因为子类可以覆写父类的方法,所以一个属性,可能对应多个 getting 方法
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null;
String propName = entry.getKey();
//是否有歧义,模棱两可
boolean isAmbiguous = false;
//遍历当前属性对应的方法集合
for (Method candidate : entry.getValue()) {
//首次进入,winner为null,将第一个方法赋值给winner,如果集合中有多个方法,第二次进来winner就不再为null
if (winner == null) {
winner = candidate;
continue;
}
//winner不等于null,获取了winner与当前候选人的返回值类型
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
//判断两者返回值类型是否相同
if (candidateType.equals(winnerType)) {
//判断返回是否是布尔类型
//只有boolean类型的返回值允许有两个相同的方法
if (!boolean.class.equals(candidateType)) {
//返回值非boolean类型,设置为有歧义
isAmbiguous = true;
break;
} else if (candidate.getName().startsWith("is")) {
//boolean类型的返回值,以is开头的方法被认为是最合适的
winner = candidate;
}
//candidateType 是 winnerType 的超类或者超接口 则winner是最合适的
} else if (candidateType.isAssignableFrom(winnerType)) {
// OK getter type is descendant
//winnerType 是 candidateType 的超类或者超接口 则candidate是最合适的
} else if (winnerType.isAssignableFrom(candidateType)) {
winner = candidate;
} else {
//上述条件都没达成,说明有歧义
isAmbiguous = true;
break;
}
}
/**
* 将解决冲突后的getting方法加入,如果是有歧义的,那就生成歧义方法调用者
*/
addGetMethod(propName, winner, isAmbiguous);
}
}
主要实现的就是在多个get方法中选择一个是当前类的get方法,如果返回值是boolean类型,则选择is开头的方法,因为这是符合规范的。若返回值类型非boolean类型,则选择类型小的那个方法,这是由java的继承决定的。但是,当返回值类型并非boolean类型,且两者返回值类型没有继承关系时,就无法选择出那个方法更适合,此时,只好生成一个歧义的方法调用者,具体实现在addGetMethod方法内部。
private void addGetMethod(String name, Method method, boolean isAmbiguous) {
//如果有歧义,就生成AmbiguousMethodInvoker(调用时会抛出自定义异常),否则生成正常方法
MethodInvoker invoker = isAmbiguous
? new AmbiguousMethodInvoker(method, MessageFormat.format(
"Illegal overloaded getter method with ambiguous type for property ''{0}'' in class ''{1}''. This breaks the JavaBeans specification and can cause unpredictable results.",
name, method.getDeclaringClass().getName()))
: new MethodInvoker(method);
getMethods.put(name, invoker);
//获取getting方法的返回数据类型
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
//根据type获取真正的class
getTypes.put(name, typeToClass(returnType));
}
关于MethodInvoker后面会讲,可以看到它是实现Invoker接口的
AmbiguousMethodInvoker继承了MethodInvoker。
根据上述方法,可以知道,若找到了合适的方法,则生成MethodInvoker,否则生成AmbiguousMethodInvoker。而AmbiguousMethodInvoker类的invoke方法的调用会直接抛出异常。
public class AmbiguousMethodInvoker extends MethodInvoker {
private final String exceptionMessage;
/**
* 歧义方法调用者的实现就是在父类实现上增加了异常的设置
* @param method
* @param exceptionMessage
*/
public AmbiguousMethodInvoker(Method method, String exceptionMessage) {
super(method);
this.exceptionMessage = exceptionMessage;
}
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
throw new ReflectionException(exceptionMessage);
}
}
addSetMethods方法和addGetMethods中很多方法是公用的。
private void addSetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingSetters = new HashMap<>();
Method[] methods = getClassMethods(clazz);
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveSetterConflicts(conflictingSetters);
}
重点看一下解决冲突的resolveSetterConflicts方法,和set方法解决冲突的实现不太一样
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
for (Entry<String, List<Method>> entry : conflictingSetters.entrySet()) {
String propName = entry.getKey();
List<Method> setters = entry.getValue();
//由getting方法返回的数据类型,来确定setting方法的参数类型
Class<?> getterType = getTypes.get(propName);
//如果getMethods中获取的Invoker是AmbiguousMethodInvoker子类,则说明当前get方法是有歧义的
boolean isGetterAmbiguous = getMethods.get(propName) instanceof AmbiguousMethodInvoker;
//默认setting方法是没有歧义的
boolean isSetterAmbiguous = false;
Method match = null;
//遍历当前属性的setting方法
for (Method setter : setters) {
//如果当前属性的get方法没有歧义,并且set方法的第一个参数类型是等于get方法的返回的类型,说明当前set方法是最优的
if (!isGetterAmbiguous && setter.getParameterTypes()[0].equals(getterType)) {
// should be the best match
match = setter;
break;
}
/**
* 如果当前的set方法没有歧义,则进行进一步的选择。
* 如果当前的set方法已经有歧义,则不再进行选择。(反正比较完了之后都会生成歧义的调用方法,后续的选择已经没有必要)
*/
if (!isSetterAmbiguous) {
/**
* 如果match、setter第一个参数类型没有任何关系,就会创建产生歧义的方法调用。
* 对于歧义的方法调用,pickBetterSetter方法会在内部将值赋给setMethods、setTypes,然后返回null
* 因此,如果match==null,就说明已经产生了歧义方法
*/
match = pickBetterSetter(match, setter, propName);
isSetterAmbiguous = match == null;
}
}
/**
* match == null 时,说明产生歧义方法,此时不需处理,因为pickBetterSetter方法已经处理了后续逻辑
* 否则,说明并非歧义方法,而是经过选择得到了唯一的最优方法,则应该做后续处理
*/
if (match != null) {
addSetMethod(propName, match);
}
}
}
与get方法不同,set方法大多返回void,而参数列表长度大多为1,并且参数类型和get方法的返回类型相同,所以此方法中也是依赖于get方法的返回值来寻找最合适的set方法。当然,如果之前没能找打合适的get方法,自然也没法找到合适的set方法。
在pickBetterSetter方法中,进行了进一步的比较。
private Method pickBetterSetter(Method setter1, Method setter2, String property) {
if (setter1 == null) {
return setter2;
}
//获取到两个方法的第一个参数的类型
Class<?> paramType1 = setter1.getParameterTypes()[0];
Class<?> paramType2 = setter2.getParameterTypes()[0];
//如果方法1的参数是方法二参数的超类或者超接口
/**
* 也就是选择类型给小的作为更合适的接口
* 因为遍历是由子类向父类进行的
*/
if (paramType1.isAssignableFrom(paramType2)) {
//则方法二是更合适的方法
return setter2;
//反之,则认为方法一更合适
} else if (paramType2.isAssignableFrom(paramType1)) {
return setter1;
}
//如果两个参数在类型上毫不相关,即没有父子类关系,则创建有歧义的方法调用者
MethodInvoker invoker = new AmbiguousMethodInvoker(setter1,
MessageFormat.format(
"Ambiguous setters defined for property ''{0}'' in class ''{1}'' with types ''{2}'' and ''{3}''.",
property, setter2.getDeclaringClass().getName(), paramType1.getName(), paramType2.getName()));
setMethods.put(property, invoker);
Type[] paramTypes = TypeParameterResolver.resolveParamTypes(setter1, type);
setTypes.put(property, typeToClass(paramTypes[0]));
return null;
}
在这个方法中,如果没有找到合适的方法,就会生成歧义方法调用者,并且内部已经处理了setMethods、setTypes,所以对外就返回null。如果找到就直接返回那个合适的方法。
private void addFields(Class<?> clazz) {
//获取所有声明的属性
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
//当前属性不存在 setMethods 中
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();
//当前属性是非final且非static,将当前field加入
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
addSetField(field);
}
}
//不存在getMethods中
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
//处理父类
if (clazz.getSuperclass() != null) {
//递归
addFields(clazz.getSuperclass());
}
}
private void addSetField(Field field) {
//先判断当前属性名是否合法
if (isValidPropertyName(field.getName())) {
//因为当前属性是不提供set方法的,所以创建的实例是 SetFieldInvoker
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));
}
}
注意,两个方法创建的Invoker对象不一样。
测试类中根据用到的不同类对象,分为几个部分,建议每个部分都进行debug,尤其是关注什么情况下会产生歧义的方法。这里贴一个测试方法。
@Slf4j
class ReflectorTest {
@Test
void shouldSettersWithUnrelatedArgTypesThrowException() throws Exception {
/**
* setProp2三个方法会产生歧义
*/
@SuppressWarnings("unused")
class BeanClass {
public void setProp1(String arg) {}
public void setProp2(String arg) {}
public void setProp2(Integer arg) {}
public void setProp2(boolean arg) {}
}
ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
Reflector reflector = reflectorFactory.findForClass(BeanClass.class);
List<String> setableProps = Arrays.asList(reflector.getSetablePropertyNames());
assertTrue(setableProps.contains("prop1"));
assertTrue(setableProps.contains("prop2"));
assertEquals("prop1", reflector.findPropertyName("PROP1"));
assertEquals("prop2", reflector.findPropertyName("PROP2"));
assertEquals(String.class, reflector.getSetterType("prop1"));
assertNotNull(reflector.getSetInvoker("prop1"));
Class<?> paramType = reflector.getSetterType("prop2");
assertTrue(String.class.equals(paramType) || Integer.class.equals(paramType) || boolean.class.equals(paramType));
//这里,因为prop2属性有三个set方法,并且三个set方法的第一个参数类型都不相同,且没有子父类关系,所以生成了歧义方法
Invoker ambiguousInvoker = reflector.getSetInvoker("prop2");
//因为是歧义方法,这里构建参数时做了判断
Object[] param = String.class.equals(paramType)? new String[]{"x"} : new Integer[]{1};
/**
* 直接调用,不捕获异常,让异常直接抛出
* {@link AmbiguousMethodInvoker#invoke(java.lang.Object, java.lang.Object[])}
* 歧义方法的invoke直接将异常抛出
*/
ambiguousInvoker.invoke(new BeanClass(), param);
/**
* junit的处理异常方式,写法蛮有意思的,可以参考下面这篇文章
* https://blog.csdn.net/dnc8371/article/details/107267815?ops_request_misc=%25257B%252522request%25255Fid%252522%25253A%252522160921991016780257451000%252522%25252C%252522scm%252522%25253A%25252220140713.130102334.pc%25255Fall.%252522%25257D&request_id=160921991016780257451000&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v29-1-107267815.pc_search_result_no_baidu_js&utm_term=com.googlecode.catchexception.apis.BDDCatchException#when
*/
when(() -> ambiguousInvoker.invoke(new BeanClass(), param));
then(caughtException()).isInstanceOf(ReflectionException.class)
.hasMessageMatching(
"Ambiguous setters defined for property 'prop2' in class '" + BeanClass.class.getName().replace("$", "\\$")
+ "' with types '(java.lang.String|java.lang.Integer|boolean)' and '(java.lang.String|java.lang.Integer|boolean)'\\.");
}
}
public class GetFieldInvoker implements Invoker {
private final Field field;
public GetFieldInvoker(Field field) {
this.field = field;
}
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException {
try {
return field.get(target);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
return field.get(target);
} else {
throw e;
}
}
}
@Override
public Class<?> getType() {
return field.getType();
}
}
public class SetFieldInvoker implements Invoker {
private final Field field;
public SetFieldInvoker(Field field) {
this.field = field;
}
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException {
try {
field.set(target, args[0]);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
field.set(target, args[0]);
} else {
throw e;
}
}
return null;
}
@Override
public Class<?> getType() {
return field.getType();
}
}
/**
* 创建MethodInvoker,说明当前方法没有歧义
* @author Clinton Begin
*/
public class MethodInvoker implements Invoker {
private final Class<?> type;
private final Method method;
public MethodInvoker(Method method) {
this.method = method;
//如果只有1个参数,说明是set方法,将参数1的类型返回
if (method.getParameterTypes().length == 1) {
type = method.getParameterTypes()[0];
//否则,就将返回值类型返回
} else {
type = method.getReturnType();
}
}
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
try {
return method.invoke(target, args);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
method.setAccessible(true);
return method.invoke(target, args);
} else {
throw e;
}
}
}
@Override
public Class<?> getType() {
return type;
}
}
public class AmbiguousMethodInvoker extends MethodInvoker {
private final String exceptionMessage;
/**
* 歧义方法调用者的实现就是在父类实现上增加了异常的设置
* @param method
* @param exceptionMessage
*/
public AmbiguousMethodInvoker(Method method, String exceptionMessage) {
super(method);
this.exceptionMessage = exceptionMessage;
}
/**
* 调用时直接抛出预设的异常
* @param target
* @param args
* @return
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
@Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
throw new ReflectionException(exceptionMessage);
}
}