Reflector analysis of Mybatis

Mybatis's MetaObject analysis: http://donald-draper.iteye.com/admin/blogs/2338818
When analyzing MetaObejct in the previous article, the implementation of MetaObejct's methods depends on ObjectWrapper, ObjectWrapper depends on MetaClass, and MetaClass depends on Reflector, which In this section, let's take a look at Reflector
//Reflector
public class Reflector
{
    private static boolean classCacheEnabled = true;
    private static final String EMPTY_STRING_ARRAY[] = new String[0];
    private static final Map REFLECTOR_MAP = new ConcurrentHashMap();
    private Class type;
    private String readablePropertyNames[];//Readable property name
    private String writeablePropertyNames[];//Writable property names
    private Map setMethods;Map<String,MethodInvoker>
    private Map getMethods;Map<String,MethodInvoker>
    private Map setTypes;Map<String,Class>
    private Map getTypes;Map<String,Class>
    private Constructor defaultConstructor;//Default constructor
    private Map caseInsensitivePropertyMap;//Case insensitive property Map
    //Construct the Reflector corresponding to the Class
    public static Reflector forClass(Class clazz)
    {
        if(classCacheEnabled)
        {
	    //If the cache state is on, get the corresponding Reflector from REFLECTOR_MAP, if not, rebuild it
            Reflector cached = (Reflector)REFLECTOR_MAP.get(clazz);
            if(cached == null)
            {
                cached = new Reflector(clazz);
                REFLECTOR_MAP.put(clazz, cached);
            }
            return cached;
        } else
        {
            return new Reflector(clazz);
        }
    }
    private Reflector(Class clazz)
    {
        readablePropertyNames = EMPTY_STRING_ARRAY;//Readable property names
        writeablePropertyNames = EMPTY_STRING_ARRAY;//Writable property names
        setMethods = new HashMap();//Set方法Map
        getMethods = new HashMap();//Get方法Map
        setTypes = new HashMap();
        getTypes = new HashMap();
        caseInsensitivePropertyMap = new HashMap();//Case-insensitive property Map
        type = clazz;
        addDefaultConstructor(clazz);
        addGetMethods(clazz);
        addSetMethods(clazz);
        addFields(clazz);
        readablePropertyNames = (String[])getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
        writeablePropertyNames = (String[])setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
        //Add the readable property to the case-insensitive property Map
	String arr$[] = readablePropertyNames;
        int len$ = arr$.length;
        for(int i$ = 0; i$ < len$; i$++)
        {
            String propName = arr$[i$];
            caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }
        //Add the writable property to the case-insensitive property Map
        arr$ = writeablePropertyNames;
        len$ = arr$.length;
        for(int i$ = 0; i$ < len$; i$++)
        {
            String propName = arr$[i$];
            caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
        }

    }
}

Let's see that addDefaultConstructor
initializes the default constructor as a no-argument constructor
private void addDefaultConstructor(Class clazz)
    {
        Constructor consts[] = clazz.getDeclaredConstructors();
        Constructor arr$[] = consts;
        int len$ = arr$.length;
        for(int i$ = 0; i$ < len$; i$++)
        {
            Constructor constructor = arr$[i$];
            if(constructor.getParameterTypes().length != 0)
                continue;
            if(canAccessPrivateMethods())
                try
                {
                    constructor.setAccessible(true);
                }
                catch(Exception e) { }
            if(constructor.isAccessible())
                defaultConstructor = constructor;
        }

    }

Look at addGetMethods
to add the Get method of the Class to
private void addGetMethods(Class cls)
    {
        Map conflictingGetters = new HashMap();
        Method methods[] = getClassMethods(cls);
        Method arr$[] = methods;
        int len$ = arr$.length;
        for(int i$ = 0; i$ < len$; i$++)
        {
            Method method = arr$[i$];
            String name = method.getName();
            if(name.startsWith("get") && name.length() > 3)
            {
                if(method.getParameterTypes().length == 0)
                {
                    name = PropertyNamer.methodToProperty(name);
		    //Add the get method corresponding to name to conflictingGetters-Map
                    addMethodConflict(conflictingGetters, name, method);
                }
                continue;
            }
            if(name.startsWith("is") && name.length() > 2 && method.getParameterTypes().length == 0)
            {
                name = PropertyNamer.methodToProperty(name);
                addMethodConflict(conflictingGetters, name, method);
            }
        }
        // resolve method conflict
        resolveGetterConflicts(conflictingGetters);
    }
     private void addMethodConflict(Map conflictingMethods, String name, Method method)
    {
        List list = (List)conflictingMethods.get(name);
        if(list == null)
        {
            list = new ArrayList();
            conflictingMethods.put(name, list);
        }
        list.add(method);
    }

     private void resolveGetterConflicts(Map conflictingGetters)
    {
        for(Iterator i$ = conflictingGetters.keySet().iterator(); i$.hasNext();)
        {
            String propName = (String)i$.next();
            List getters = (List)conflictingGetters.get(propName);
            Iterator iterator = getters.iterator();
            Method firstMethod = (Method)iterator.next();
            if(getters.size() == 1)
            {
                addGetMethod(propName, firstMethod);
            } else
            {
                Method getter = firstMethod;
                Class getterType = firstMethod.getReturnType();
                do
                {
                    if(!iterator.hasNext())
                        break;
                    Method method = (Method)iterator.next();
                    Class methodType = method.getReturnType();
                    if(methodType.equals(getterType))
                        throw new ReflectionException((new StringBuilder()).append("Illegal overloaded getter method with ambiguous type for property ").append(propName).append(" in class ").append(firstMethod.getDeclaringClass()).append(".  This breaks the JavaBeans ").append("specification and can cause unpredicatble results.").toString());
                    if(!methodType.isAssignableFrom(getterType))
                        if(getterType.isAssignableFrom(methodType))
                        {
                            getter = method;
                            getterType = methodType;
                        } else
                        {
                            throw new ReflectionException((new StringBuilder()).append("Illegal overloaded getter method with ambiguous type for property ").append(propName).append(" in class ").append(firstMethod.getDeclaringClass()).append(".  This breaks the JavaBeans ").append("specification and can cause unpredicatble results.").toString());
                        }
                } while(true);
		//Add the get method corresponding to name to the getMethods collection
                addGetMethod(propName, getter);
            }
        }

    }
    //Add the get method corresponding to name to the getMethods collection
    private void addGetMethod(String name, Method method)
    {
        if(isValidPropertyName(name))
        {
            getMethods.put(name, new MethodInvoker(method));
            getTypes.put(name, method.getReturnType());
        }
    }

look at addSetMethods
private void addSetMethods(Class cls)
    {
        Map conflictingSetters = new HashMap();
        Method methods[] = getClassMethods(cls);
        Method arr$[] = methods;
        int len$ = arr$.length;
        for(int i$ = 0; i$ < len$; i$++)
        {
            Method method = arr$[i$];
            String name = method.getName();
            if(name.startsWith("set") && name.length() > 3 && method.getParameterTypes().length == 1)
            {
                name = PropertyNamer.methodToProperty(name);
		//Add the set method corresponding to name to conflictingSetters-Map
                addMethodConflict(conflictingSetters, name, method);
            }
        }
        // resolve method conflict
        resolveSetterConflicts(conflictingSetters);
    }

    private void addMethodConflict(Map conflictingMethods, String name, Method method)
    {
        List list = (List)conflictingMethods.get(name);
        if(list == null)
        {
            list = new ArrayList();
            conflictingMethods.put(name, list);
        }
        list.add(method);
    }

    private void resolveSetterConflicts(Map conflictingSetters)
    {
        Iterator i$ = conflictingSetters.keySet().iterator();
        do
        {
            if(!i$.hasNext())
                break;
            String propName = (String)i$.next();
            List setters = (List)conflictingSetters.get(propName);
            Method firstMethod = (Method)setters.get(0);
            if(setters.size() == 1)
            {
                addSetMethod(propName, firstMethod);
                continue;
            }
            Class expectedType = (Class)getTypes.get(propName);
            if(expectedType == null)
                throw new ReflectionException((new StringBuilder()).append("Illegal overloaded setter method with ambiguous type for property ").append(propName).append(" in class ").append(firstMethod.getDeclaringClass()).append(".  This breaks the JavaBeans ").append("specification and can cause unpredicatble results.").toString());
            Iterator methods = setters.iterator();
            Method setter = null;
            do
            {
                if(!methods.hasNext())
                    break;
                Method method = (Method)methods.next();
                if(method.getParameterTypes().length != 1 || !expectedType.equals(method.getParameterTypes()[0]))
                    continue;
                setter = method;
                break;
            } while(true);
            if(setter == null)
                throw new ReflectionException((new StringBuilder()).append("Illegal overloaded setter method with ambiguous type for property ").append(propName).append(" in class ").append(firstMethod.getDeclaringClass()).append(".  This breaks the JavaBeans ").append("specification and can cause unpredicatble results.").toString());
            //Add the corresponding set method to the setMethods-map
	    addSetMethod (propName, setter);
        } while(true);
    }

    private void addSetMethod(String name, Method method)
    {
        if(isValidPropertyName(name))
        {
            setMethods.put(name, new MethodInvoker(method));
            setTypes.put(name, method.getParameterTypes()[0]);
        }
    }

look at addFields
private void addFields(Class clazz)
    {
        Field fields[] = clazz.getDeclaredFields();
        Field arr$[] = fields;
        int len$ = arr$.length;
        for(int i$ = 0; i$ < len$; i$++)
        {
            Field field = arr$[i$];
            if(canAccessPrivateMethods())
                try
                {
                    field.setAccessible(true);
                }
                catch(Exception e) { }
            if(!field.isAccessible())
                continue;
            if(!setMethods.containsKey(field.getName()))
                addSetField(field);
            if(!getMethods.containsKey(field.getName()))
                addGetField(field);
        }

        if(clazz.getSuperclass() != null)
            addFields(clazz.getSuperclass());
    }
    //Add Field's corresponding set method and parameter type to setMethods, setTypes-Map
    private void addSetField(Field field)
    {
        if(isValidPropertyName(field.getName()))
        {
            setMethods.put(field.getName(), new SetFieldInvoker(field));
            setTypes.put(field.getName(), field.getType());
        }
    }
    //Add Field's corresponding get method and parameter type to setMethods, setTypes-Map
    private void addGetField(Field field)
    {
        if(isValidPropertyName(field.getName()))
        {
            getMethods.put(field.getName(), new GetFieldInvoker(field));
            getTypes.put(field.getName(), field.getType());
        }
    }

Summary:
From the above analysis, the work done by the Reflector constructor is as an initial constructor, adding the set and get methods and parameters of Class to the Map corresponding to setMethods, getMethods, setTypes, and getTypes, and the key attribute of setMethods and getMethods Map Name, value is the corresponding method, and then initialize the readable, writable, and case-insensitive attribute set.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326941223&siteId=291194637