Java reflection principle analysis - Method articles

Analysis of the reflection method

ClassObject provides the following methods for acquiring an object ( Method):

  • getMethod - specific method returns the class or interface, the public can only be modified method.
  • getDeclaredMethod - specific method returns the class or interface.
  • getMethods - All public class or interface method returns, including public methods of the parent class.
  • getDeclaredMethods - returns the class or interface's method, including public, protected, default (package) access, and private methods, but does not include inherited methods.

Test Case

//父类
public class RefFather {
    public void refFatherMethod(){
        System.out.println("我是父类方法 refFatherMethod");
    }
}

//子类
public class RefSon extends RefFather{
    private void refSonMethod(){
        System.out.println("我是子类方法 refMethod");
    }
}

//测试方法
public static void main(String[] args) {
    try {
            //类的全限定名 即包名+类名
            Class<?> clz = Class.forName("main.ref.RefSon");
            Object obj = clz.newInstance();
            Method refFatherMethod = clz.getMethod("refFatherMethod");
            Method refSonMethod = clz.getDeclaredMethod("refSonMethod");
            refSonMethod.invoke(obj);
     } catch (Exception e) {
            e.printStackTrace();
     }
}
复制代码
Method

Look at the Class.getMethod(String name, Class<?>... parameterTypes)source:

    @CallerSensitive
    public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Method method = getMethod0(name, parameterTypes, true);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
复制代码

The above source we just need to focus on resolving the following questions can know getMethodthe method exactly how to achieve.

  1. @CallerSensitive
  2. checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
  3. Reflection.getCallerClass()
  4. Method method = getMethod0(name, parameterTypes, true);
@CallerSensitive

In fact, this annotation is used Java bug fixes. Preventing the user from using a double reflection to elevate privileges, whether there is a principle that should be checked only when the reflection of the depth of the caller class privilege, I had class itself is not so high authority, but I can be invoked by multiple reflections increase authority.

For chestnuts, such as the reflection of a class method requires looking up a fixed two-caller has permission (reflecting the relevant class privilege is relatively high), I can pass my own class -> reflection 1--> reflection such a call chain 2, 2 check permissions reflection reflection 1, resulting in vulnerabilities. The use of such notes, it getCallerClasswill skip has @CallerSensitivemodified the interface methods, direct look real caller ( actual caller).

checkMemberAccess

Look at the source code of this method:

private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {
        final SecurityManager s = System.getSecurityManager();
        if (s != null) {
            final ClassLoader ccl = ClassLoader.getClassLoader(caller);
            final ClassLoader cl = getClassLoader0();
            if (which != Member.PUBLIC) {
                if (ccl != cl) {
                    s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
                }
            }
            this.checkPackageAccess(ccl, checkProxyInterfaces);
        }
    }
复制代码

His main function is to check whether to allow clients to access members.

Default policies: Allow all clients to use plain Java Access access control.

What does that mean?

That is, if you do not take the initiative to configure SecurityManagersecurity manager, in accordance with the access modifier Class class to determine whether there is authority. For example, protectedallow only the same package or subclasses access, cross-package it into a private disable access.

final ClassLoader ccl = ClassLoader.getClassLoader(caller);

final ClassLoader cl = getClassLoader0();

If you have a custom SecurityManager(calls System.setSecurityManager(new MySecurityManager())), the caller will be to determine whether the parties with access to the same class loader, if there is, if not the public access modifier, there is no limit privileges. Under normal circumstances where the class loader ClassLoaderis the application class loader Application ClassLoader.

Here briefly Application ClassLoader:

This class loader is responsible for loading the user class path in the library (CLASSPATH), generally we write java classes are loaded by the class loader, the class loader is the return value getSystemClassLoader CLassLoader in () method, so it the system is called class loader. this is the default class loader in general.

Reflection.getCallerClass()

Continue to look at the source code:

    /** @deprecated */
    @Deprecated
    public static native Class<?> getCallerClass(int var0);
复制代码

native representative of this method is actually implemented in other languages, so only illustrate the use of here.

Brief Reflection.getCallerClass(int var0), we are interested can find out.

If the value var0 equal to 0 or less, the process returns class sun.reflect.Reflection;

If the value is equal to 1 var0 then return back to their class. That is what this line of code inside the class, returns the class.

If the value is equal to 2 var0, returns the caller's class is returned. For chestnuts, this method methodA A, call the methodA A in the method B, this time Reflection.getCallerClass(int var0)is the return of class B.

getMethod0(name, parameterTypes, true)

Continued Source OvO:

private Method getMethod0(String name, Class<?>[] parameterTypes, boolean includeStaticMethods) {
        MethodArray interfaceCandidates = new MethodArray(2);
        Method res =  privateGetMethodRecursive(name, parameterTypes, includeStaticMethods, interfaceCandidates);
        if (res != null)
            return res;

        // Not found on class or superclass directly
        interfaceCandidates.removeLessSpecifics();
        return interfaceCandidates.getFirst(); // may be null
    }

 private Method privateGetMethodRecursive(String name,
            Class<?>[] parameterTypes,
            boolean includeStaticMethods,
            MethodArray allInterfaceCandidates) {
        Method res;
        // Search declared public methods
        if ((res = searchMethods(privateGetDeclaredMethods(true),
                                 name,
                                 parameterTypes)) != null) {
            if (includeStaticMethods || !Modifier.isStatic(res.getModifiers()))
                return res;
        }
        // Search superclass's methods
        if (!isInterface()) {
            Class<? super T> c = getSuperclass();
            if (c != null) {
                if ((res = c.getMethod0(name, parameterTypes, true)) != null) {
                    return res;
                }
            }
        }
        // Search superinterfaces' methods
        Class<?>[] interfaces = getInterfaces();
        for (Class<?> c : interfaces)
            if ((res = c.getMethod0(name, parameterTypes, false)) != null)
                allInterfaceCandidates.add(res);
        // Not found
        return null;
    }

	private static Method searchMethods(Method[] methods,
                                        String name,
                                        Class<?>[] parameterTypes)
    {
        Method res = null;
        String internedName = name.intern();
        for (int i = 0; i < methods.length; i++) {
            Method m = methods[i];
            if (m.getName() == internedName
                && arrayContentsEq(parameterTypes, m.getParameterTypes())
                && (res == null
                    || res.getReturnType().isAssignableFrom(m.getReturnType())))
                res = m;
        }

        return (res == null ? res : getReflectionFactory().copyMethod(res));
    }

  private Method[] privateGetDeclaredMethods(boolean publicOnly) {
        checkInitted();
        Method[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
        if (rd != null) {
            if (publicOnly) {
                rd.declaredPublicMethods = res;
            } else {
                rd.declaredMethods = res;
            }
        }
        return res;
    }
复制代码

When you call getMethod0, he called Class proprietary method privateGetMethodRecursiverecursively get all this class of publicmodified methods (containing parent).

In privateGetMethodRecursivethe current class will first of all methods to match, if there is no will to find his parent, if the parent does not will continue recursively until the top-level parent class Object. If there is a copy of this Method is returned.

privateGetDeclaredMethods(boolean publicOnly) Parameter is true, all public methods of this class is returned, otherwise all methods modifier.

According to privateGetDeclaredMethods, it is not difficult to understand why getMethods () returns all public methods include the parent class, and getDeclaredFields()get all the modifier method of the current method.

Boldly speculate about, getMethods()should be a recursive call privateGetDeclaredMethods(true)method, but getDeclaredMethods()should only be called once privateGetDeclaredMethods(false)method.

getMethods

Look at the source:

    @CallerSensitive
    public Method[] getMethods() throws SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return copyMethods(privateGetPublicMethods());
    }
    
    private Method[] privateGetPublicMethods() {
        checkInitted();
        Method[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {
            res = rd.publicMethods;
            if (res != null) return res;
        }
      
      	//没有可用的缓存值;递归计算值。
				//从获取公共声明的方法开始
      	MethodArray methods = new MethodArray();
        {
            //请注意这里
            Method[] tmp = privateGetDeclaredMethods(true);
            methods.addAll(tmp);
        }
        ...
        return res;
    }
复制代码
getDeclaredMethods

Source as follows:

    @CallerSensitive
    public Method[] getDeclaredMethods() throws SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        //请注意这里
        return copyMethods(privateGetDeclaredMethods(false));
    }
复制代码

Sure enough, both expected and in the same.

Finally, there is a getDeclaredMethodmethod does not describe, but I believe we must read the above source code would have guessed that the implementation process.

    @CallerSensitive
    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
        Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
        if (method == null) {
            throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
        }
        return method;
    }
复制代码

All of the above on access to source code analysis methods have been combing Class finished ~ (@ ^ _ ^ @) ~ like it also requested a top praise, your support is the greatest force me updated.

Due to the limited technical writer, the article inevitably slightly wrong, welcome ━ ( `∀' ) Techno correct me!

Reference material

jvm comment @CallerSensitive usefulness

Everyone says low reflection efficiency of Java, where you know what the reason

Guess you like

Origin juejin.im/post/5da6be7a6fb9a04dee182727