本文主要介绍MyBatis的反射模块是如何实现的。apache
MyBatis 反射的核心类Reflector,下面我先说明它的构造函数和成员变量。具体方法下面详解。缓存
org.apache.ibatis.reflection.Reflector
public class Reflector { private final Class<?> type; //对应的Class 类型 //可读属性的名称集合,可读属性就是存在相应getter 方法的属性,初始值为空数纽 private final String[] readablePropertyNames; //可写属性的名称集合,可写属性就是存在相应setter 方法的属性,初始值为空数纽 private final String[] writeablePropertyNames; //记录了属性相应的setter 方法, key 是属性名称, value 是Invoker 对象,它是对setter 方法对应 private final Map<String, Invoker> setMethods = new HashMap<>(); //记录了属性相应的getter 方法, key 是属性名称, value 是Invoker 对象,它是对setter 方法对应 private final Map<String, Invoker> getMethods = new HashMap<>(); //记录了属性相应的setter 方法的参数值类型, ke y 是属性名称, value 是setter 方法的参数类型 private final Map<String, Class<?>> setTypes = new HashMap<>(); //记录了属性相应的getter 方法的返回位类型, key 是属性名称, value 是getter 方法的返回位类型 private final Map<String, Class<?>> getTypes = new HashMap<>(); //记录了默认构造方法 private Constructor<?> defaultConstructor; //记录了全部属性名称的集合 private Map<String, String> caseInsensitivePropertyMap = new HashMap<>(); public Reflector(Class<?> clazz) { type = clazz; //查找clazz的无参构造方法,经过反射遍历全部构造方法,找到构造参数集合长度为0的。 addDefaultConstructor(clazz); //处理clazz 中的getter 方法,填充getMethods 集合和getTypes 集合 addGetMethods(clazz); //处理clazz 中的set ter 方法,填充setMethods 集合和set Types 集合 addSetMethods(clazz); //处理没有get/set的方法字段 addFields(clazz); //初始化可读写的名称集合 readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); //初始化caseInsensitivePropertyMap ,记录了全部大写格式的属性名称 for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writeablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } } 。。。。。。。。。。。。具体代码先忽略,经过构造函数的调用慢慢渗透。 }
1:addDefaultConstructor() // 查找clazz的无参构造方法,经过反射遍历全部构造方法,找到构造参数集合长度为0的。
主要实现的思想是,经过 clazz.getDeclaredConstructors();获取全部构造方法集合,而后循环遍历 判断参数长度为0的,而且构造函数权限可控制的设为默认构造方法。mybatis
private void addDefaultConstructor(Class<?> clazz) {
Constructor<?>[] consts = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : consts) {
if (constructor.getParameterTypes().length == 0) {
//判断反射对象的控制权限 为true是可控制
if (canControlMemberAccessible()) {
try {
//设置Accessible为true后,反射能够访问私有变量。
constructor.setAccessible(true);
} catch (Exception e) {
// Ignored. This is only a final precaution, nothing we can do.
}
}
if (constructor.isAccessible()) {
this.defaultConstructor = constructor;
}
}
}
}
2:addGetMethods(clazz)// 处理clazz 中的getter 方法,填充getMethods 集合和getTypes 集合ide
private void addGetMethods(Class<?> cls) { Map<String, List<Method>> conflictingGetters = new HashMap<>(); //获取当前类以及父类中定义的全部方法的惟一签名以及相应的Method对象。 Method[] methods = getClassMethods(cls); for (Method method : methods) { if (method.getParameterTypes().length > 0) { continue; } String name = method.getName(); //判断若是方法明是以get开头而且方法名长度大于3 或者 方法名是以is开头而且长度大于2 if ((name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2)) { //将方法名截取,若是是is从第二位截取,若是是get或者set从第三位开始截取 name = PropertyNamer.methodToProperty(name); //addMethodConflict 方法内部很简单只有两行代码: //1:List<Method> list=conflictingGetters.computeIfAbsent(name,K->new ArrayList<>()); 这句话的意思是,在conflictingGetters 的Map中 若是key中存在name,name什么都不作,将value返回,若是name不存在,则返回一个新的ArrayList. //2:list.add(method); 将方法对象添加到list对象中。 addMethodConflict(conflictingGetters, name, method); } } resolveGetterConflicts(conflictingGetters); }
2-1:getClassMethods(cls);//获取当前类以及父类中定义的全部方法的惟一签名以及相应的Method对象。函数
private Method[] getClassMethods(Class<?> cls) { Map<String, Method> uniqueMethods = new HashMap<>(); Class<?> currentClass = cls; while (currentClass != null && currentClass != Object.class) { //currentClass.getDeclaredMethods(),获取当前类的全部方法 //addUniqueMethods 为每一个方法生成惟一签名,并记录到uniqueMethods集合中 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[methods.size()]); }
2-1-1: addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); 为每一个方法生成惟一签名,并记录到uniqueMethods集合中 this
private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) { for (Method currentMethod : methods) { //判断是否是桥接方法, 桥接方法是 JDK 1.5 引入泛型后,为了使Java的泛型方法生成的字节码和 1.5 版本前的字节码相兼容,由编译器自动生成的方法 if (!currentMethod.isBridge()) { //获取签名 // 签名格式为:方法返回参数#方法名:参数名 ps:多个参数用,分割 签名样例:String#getName:User 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 //若是签名存在,则不作处理,表示子类已经覆盖了该方法。 //若是签名不存在,则将签名做为Key,Method做为value 添加到uniqueMethods中 if (!uniqueMethods.containsKey(signature)) { if (canControlMemberAccessible()) { try { currentMethod.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } uniqueMethods.put(signature, currentMethod); } } } }
2-2: resolveGetterConflicts(conflictingGetters);;//在2-1中返回的方法可能存在,两个相同的方法名称,由于当子类实现父类方法时且参数不一样,此时2-1生成的签名是不一样的生成签名的规则是 方法返回值#方法名#参数名,那么就会返回两个相同的方法名。 resolveGetterConflicts方法会对这种覆写的状况进行处理,同时将处理后的getter方法记录到getMethods集合中,将其返回值类型填充到getTypes集合中。 内部实现主要是两个for循环,循环比较方法名称相同的状况下,返回值不一样的状况下,拿第二个当最终想要的Method。spa
private void resolveGetterConflicts(Map<String, List<Method>> 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); } }
总结一下addGetMethods(clazz)方法 addSetMethods(clazz)大体相同:xml
首先建立:对象
Map<String, List<Method>> conflictingGetters = new HashMap<>();
1:获取子类和父类的全部方法。 获取方法是 先生成惟一签名,惟一签名规则是方法返回值#方法名:方法参数1,方法参数2 。 根据签名做为key,method对象做为value生成Map,经过签名进行过滤,将此M,ap转换为List返回。接口
2:循环遍历Map,找到符合条件的方法名,is开头或者get开头的,将方法名截取,截取后的方法名做为key,List<Method>做为value,放入到conflictingGetters中。
3:因为子类存在实现父类方法,且返回值不一样的状况,致使用一方法名 可能有不一样的Method ,第三步 resolveGetterConflicts方法会对这种覆写的状况进行处理,同时将处理后的getter方法记录到getMethods集合中,将其返回值类型填充到getTypes集合中。
Reflector Factory 接口主要实现了对Reflector 对象的建立和缓存,有三个方法:该接口定义以下:
public interface ReflectorFactory { boolean isClassCacheEnabled(); //检测该ReflectorFactory对象是否会缓存Reflector对象 void setClassCacheEnabled(boolean classCacheEnabled);//设置是否缓存Reflector对象 Reflector findForClass(Class<?> type); //建立指定class对应的Reflector对象 }
Reflector Factory的实现是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) { //若是开启缓存,Reflector对象从ConcurrentMap<Class<?>, Reflector> 取出。 if (classCacheEnabled) { // synchronized (type) removed see issue #461 return reflectorMap.computeIfAbsent(type, Reflector::new); } else {//没开启缓存,从新建立。 return new Reflector(type); } } }
DefaultReflectorFactory 的缓存是经过ConcurrentMap来实现的,若是开启了缓存,那么就从ConcurrentMap取Reflector,若是没有开启,就新建Reflector.
除了使用MyBatis 提供的DefaultReflectorFactory 实现,咱们还能够在mybatis-config .xml中配置自定义的ReflectorFactory 实现类,从而实现功能上的扩展。