java source code analysis - Reflection Class

java source code analysis - Reflection Class

1. What is class

Class is essentially a class of java. This class extracts and abstracts the relevant information of all classes in java. Simply put, the class class represents the type information of the created class. Class objects can provide the type information of a class object when allowed, including a series of operations on constructors, methods, variables and so on.

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement 

2. How to get the Class object

There are three ways to get the Class object of a Class.

2.1 getclass() method

The Class object can be obtained through the getClass() method of the object instance.

public final native Class<?> getClass();

This is a native method in the Object class.

ClassDemo classDemo = new ClassDemo();
Class clazz = classDemo.getClass();
System.out.println(clazz);

2.2class attribute

The class object can be obtained through the class attribute of the class.

Class clazz1 = ClassDemo.class;
System.out.println(clazz1);

2.3 pass the forName() method of Class

public static Class<?> forName(String className)
                throws ClassNotFoundException
 Class clazz2 = Class.forName("test.java.lang.reflect.ClassDemo");
 System.out.println(clazz2);

3. Common methods

3.1 get constructor object

(1)getDeclaredConstructors()

Get all constructors of a class, including private constructors and public constructors.

public Constructor<?>[] getDeclaredConstructors() throws SecurityException

(2)getConstructors()

Gets the public constructor of a class.

public Constructor<?>[] getConstructors() throws SecurityExceptionp

(3)getConstructor(...)

Gets the specified constructor of a class according to the parameter list type.

public Constructor<T> getConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException

Test:

Define a ClassDemo class:

public class ClassDemo extends ClassDemoPareson implements IClassDemo {


    public ClassDemo() {
    }

    public static final Integer MIN = 0;

    public String age;

    private String name;

    public ClassDemo(String name){
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public int getNum() {
        return 0;
    }

    public void setName(String name) {
        this.name = name;
    }

    private String test(String arg){
        return null;
    }
}    

Test class:

package test.java.lang.reflect;

import java.lang.reflect.Constructor;
import java.util.Arrays;

public class ClassTest {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
//        ClassDemo classDemo = new ClassDemo();
//        Class clazz = classDemo.getClass();
//        System.out.println(clazz);
//        Class clazz1 = ClassDemo.class;
//        System.out.println(clazz1);
//        Class clazz2 = Class.forName("test.java.lang.reflect.ClassDemo");
//        System.out.println(clazz2);
        ClassDemo classDemo = new ClassDemo();
        Class clazz = classDemo.getClass();
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        System.out.println(Arrays.toString(declaredConstructors));
        Constructor[] constructors = clazz.getConstructors();
        System.out.println(Arrays.toString(constructors));
        Constructor constructor = clazz.getConstructor(String.class);
        System.out.println(constructor);
    }
}

result:

3.2 get member method object

(1)getDeclaredMethods()

Get all method objects defined by a class, including private methods, public methods and static methods.

public Method[] getDeclaredMethods() throws SecurityException

(2)getMethods()

Get all public method objects.

public Method[] getMethods() throws SecurityException

(3)getMethod(...)

Get the specified Method object according to the Method name and parameter list.

public Method getMethod(String name, Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException

Test:

Method[] declaredMethods = clazz.getDeclaredMethods();
System.out.println(Arrays.toString(declaredMethods));
Method[] methods = clazz.getMethods();
System.out.println(Arrays.toString(methods));
Method setNameMethod = clazz.getMethod("setName", String.class);
System.out.println(setNameMethod);

result:

3.3 get member variable object

(1)getDeclaredFields()

Get all member variable Field objects, including private, public and static.

public Field[] getDeclaredFields() throws SecurityException

(2)getFields()

Gets the public member variable object

public Field[] getFields() throws SecurityException

(3)getDeclaredField(...)

Gets the variable name based on the object.

public Field getDeclaredField(String name)
        throws NoSuchFieldException, SecurityException

Test:

Field[] declaredFields = clazz.getDeclaredFields();
System.out.println(Arrays.toString(declaredFields));
Field[] fields = clazz.getFields();
System.out.println(Arrays.toString(fields));
Field name = clazz.getDeclaredField("name");
System.out.println(name);

result:

3.4 other methods

(1)getModifiers()

Gets the integer value of the language modifier of a class.

java language descriptors include:

  • Accessible descriptor:

    private, public, protected, default

  • Inaccessible descriptor

    ​ static,final,abstract,synchronized,volatile...

Each descriptor corresponds to an integer value. Its specific value is in Java lang.reflect. There are definitions in the modifier class.

 /**
     * The {@code int} value representing the {@code public}
     * modifier.
     */
    public static final int PUBLIC           = 0x00000001;

    /**
     * The {@code int} value representing the {@code private}
     * modifier.
     */
    public static final int PRIVATE          = 0x00000002;

    /**
     * The {@code int} value representing the {@code protected}
     * modifier.
     */
    public static final int PROTECTED        = 0x00000004;

    /**
     * The {@code int} value representing the {@code static}
     * modifier.
     */
    public static final int STATIC           = 0x00000008;

    /**
     * The {@code int} value representing the {@code final}
     * modifier.
     */
    public static final int FINAL            = 0x00000010;

    /**
     * The {@code int} value representing the {@code synchronized}
     * modifier.
     */
    public static final int SYNCHRONIZED     = 0x00000020;

    /**
     * The {@code int} value representing the {@code volatile}
     * modifier.
     */
    public static final int VOLATILE         = 0x00000040;

    /**
     * The {@code int} value representing the {@code transient}
     * modifier.
     */
    public static final int TRANSIENT        = 0x00000080;

    /**
     * The {@code int} value representing the {@code native}
     * modifier.
     */
    public static final int NATIVE           = 0x00000100;

    /**
     * The {@code int} value representing the {@code interface}
     * modifier.
     */
    public static final int INTERFACE        = 0x00000200;

    /**
     * The {@code int} value representing the {@code abstract}
     * modifier.
     */
    public static final int ABSTRACT         = 0x00000400;

    /**
     * The {@code int} value representing the {@code strictfp}
     * modifier.
     */
    public static final int STRICT           = 0x00000800;

    // Bits not (yet) exposed in the public API either because they
    // have different meanings for fields and methods and there is no
    // way to distinguish between the two in this class, or because
    // they are not Java programming language keywords
    static final int BRIDGE    = 0x00000040;
    static final int VARARGS   = 0x00000080;
    static final int SYNTHETIC = 0x00001000;
    static final int ANNOTATION  = 0x00002000;
    static final int ENUM      = 0x00004000;
    static final int MANDATED  = 0x00008000;

class. The getmodifiers () method is to get this integer value. It is a local method.

public native int getModifiers();

(2)isInterface()

Determine whether this class is an interface.

public native boolean isInterface();

(3)getName()

Gets the full pathname of the class.

public String getName()

(4)getSuperclass()

Gets the parent Class type of the Class.

public native Class<? super T> getSuperclass();	

Test:

int modifiers = clazz.getModifiers();
System.out.println(modifiers);
boolean anInterface = clazz.isInterface();
System.out.println(anInterface);
String clazzName = clazz.getName();
System.out.println(clazzName);
Class clazzSuperclass = clazz.getSuperclass();
System.out.println(clazzSuperclass);

result:

There are many other methods, not one by one.

4. Key analysis

Let's focus on the source code for the following methods.

4.1 getconstructors() method

As mentioned earlier, getConstructors() is the public Constructor object of the class.

public Constructor<?>[] getConstructors() throws SecurityException {    //Get public constructor public
    checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);//(1)
    return copyConstructors(privateGetDeclaredConstructors(true));//(2)
}

getConstructors is mainly divided into two parts: one is to verify member permissions, and the other is to obtain Constructor objects.

(1) Verification authority

private void checkMemberAccess(int which, Class<?> caller, boolean checkProxyInterfaces) {  //Set access permissions for members
        final SecurityManager s = System.getSecurityManager();  //Get the security manager. It is closed by default, so it is null here
        if (s != null) {
            /* Default policy allows access to all {@link Member#PUBLIC} members,
             * as well as access to classes that have the same class loader as the caller.
             * In all other cases, it requires RuntimePermission("accessDeclaredMembers")
             * permission.
             */
            final ClassLoader ccl = ClassLoader.getClassLoader(caller);//Gets the class loader of the calling class
            final ClassLoader cl = getClassLoader0();
            if (which != Member.PUBLIC) {   //If it is not a public access identifier
                if (ccl != cl) {
                    s.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);    //Set permissions
                }
            }
            this.checkPackageAccess(ccl, checkProxyInterfaces); //Set access permissions for packages
        }
    }

SecurityManager class: This is a java security manager that allows applications to implement security policies. See the "java reflection source code analysis - SecurityManager" for details.

Simply put, this code is to check whether the program operation has the corresponding permission. Generally, the security manager is not started by default.

(2) Get Constructor object

First look at the privateGetDeclaredConstructors() method

private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {   //Get constructor (with and without parameters)
        checkInitted();// 1
        Constructor<T>[] res;
        ReflectionData<T> rd = reflectionData();
        if (rd != null) {	//2
            res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
            if (res != null) return res;
        }
        // No cached value available; request value from VM
        if (isInterface()) {    //3
            @SuppressWarnings("unchecked")
            Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
            res = temporaryRes; 
        } else {	//4
            res = getDeclaredConstructors0(publicOnly);
        }
        if (rd != null) {	//5
            if (publicOnly) {
                rd.publicConstructors = res;
            } else {
                rd.declaredConstructors = res;
            }
        }
        return res;
    }

To summarize the privateGetDeclaredConstructors() method:

1) Initialize the verification and check whether the relevant configurations in the system have been loaded;

2) Get the ReflectionData object, which is used to cache relevant data such as constructor; If there is a cache, the corresponding data is directly obtained from the cache and returned;

// Lazily create and cache ReflectionData
    private ReflectionData<T> reflectionData() {        //Lazy creation and caching of reflected data
        SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;  //Create a ReflectionData object by using soft reference. / / soft reference: this object will be recycled when memory is insufficient
        int classRedefinedCount = this.classRedefinedCount; //Sets the number of redefinitions
        ReflectionData<T> rd;
        if (useCaches &&                                    //Enable caching
            reflectionData != null &&                       //Soft reference cannot be empty
            (rd = reflectionData.get()) != null &&          //ReflectionData object is not empty
            rd.redefinedCount == classRedefinedCount) {     //Redefinitedcount value is equal to classredefinitedcount
            return rd;
        }
        // else no SoftReference or cleared SoftReference or stale ReflectionData
        // -> create and replace new instance
        return newReflectionData(reflectionData, classRedefinedCount);//Otherwise, create a new cache
    }

3) Judge whether the Class represented by the Class object is an interface. If so, return an empty Constructor array object, because the interface has no Constructor;

4) If it is not an interface, the constructor will be obtained through the local method getdeclaraedconstructors0;

private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);

5) Save the obtained data to the cache object ReflectionData.

After obtaining the Constructor, it is not directly returned to the caller, but returned by copying. Take a look at the source code of copyConstructors

private static <U> Constructor<U>[] copyConstructors(Constructor<U>[] arg) {//Copy constructors
        Constructor<U>[] out = arg.clone();//Get a copy of the Constructor
        ReflectionFactory fact = getReflectionFactory();    //Get reflection factory
        for (int i = 0; i < out.length; i++) {  
            out[i] = fact.copyConstructor(out[i]);//Copy Constructor through reflection factory
        }
        return out;
    }

You can see that a copy of the Constructors is returned to the caller through the copyConstructors method. In this way, any changes made by the caller to the Constructor will not affect the original Constructor.

4.2 newinstance() method

This method is also an important method of class object. Its function is to create an instance object according to class type.

public T newInstance()
        throws InstantiationException, IllegalAccessException
    {   
        if (System.getSecurityManager() != null) {  //1
            checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), false);
        }

        // NOTE: the following code may not be strictly correct under
        // the current Java memory model.

        // Constructor lookup
        if (cachedConstructor == null) {    //2
            if (this == Class.class) {
                throw new IllegalAccessException(   
                    "Can not call newInstance() on the Class for java.lang.Class"
                );
            }
            try {
                Class<?>[] empty = {};
                final Constructor<T> c = getConstructor0(empty, Member.DECLARED);
                // Disable accessibility checks on the constructor
                // since we have to do the security check here anyway
                // (the stack depth is wrong for the Constructor's
                // security check to work)
                java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction<Void>() {
                        public Void run() {
                                c.setAccessible(true);  
                                return null;
                            }
                        });
                cachedConstructor = c;
            } catch (NoSuchMethodException e) {
                throw (InstantiationException)
                    new InstantiationException(getName()).initCause(e);
            }
        }
        Constructor<T> tmpConstructor = cachedConstructor;	
        // Security check (same as in java.lang.reflect.Constructor)
        int modifiers = tmpConstructor.getModifiers(); 
        if (!Reflection.quickCheckMemberAccess(this, modifiers)) {	//3
            Class<?> caller = Reflection.getCallerClass();  
            if (newInstanceCallerCache != caller) { 
                Reflection.ensureMemberAccess(caller, this, null, modifiers);   
                newInstanceCallerCache = caller;
            }
        }
        // Run constructor
        try {
            return tmpConstructor.newInstance((Object[])null);  //4
        } catch (InvocationTargetException e) {
            Unsafe.getUnsafe().throwException(e.getTargetException());
            // Not reached
            return null;
        }
    }

We divide the newInstance method into four parts. The main work is as follows:

(1) Determine the verification authority according to the SecurityManager;

(2) Get the cachedConstructor cache object, call the getConstructor0 method to get the constructor and put it into the cache to facilitate the next direct acquisition;

(3) Set member access rights;

(4) Call the newInstance method of the Constructor object to get the instance object.

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {   //Construct the instance object of the class corresponding to the constructor through the parameter list
        if (!override) {//This flag indicates whether to override the access check permission at the language level. The initial false can be passed through constructor1 Setaccessible (true) is set to true
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();  //Get call class
                checkAccess(caller, clazz, null, modifiers);//Verification authority
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)    //Whether the modifier is an enumeration type
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();  //Gets the accessor of the constructor
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);//Instantiate the object by declaring the newInstance of the class through the constructor
        return inst;
    }

As for constructoraccessor The newinstance () method has been described in detail in java reflection source code analysis Constructor, so we won't talk about it here.

5. Related categories

5.1ReflectionFactory

Through the class name, we can roughly know that this is a factory class for reflection. Usually, factories are used to instantiate specific products and create class objects. However, ReflectionFactory is a little different. It creates the emitted core objects Field, Constructor and Method through deep cloning. Not create, but copy. Let's take a look at his three cloning methods:

Let's take a look at the NewField() method:

 public Field newField(Class<?> var1, String var2, Class<?> var3, int var4, int var5, String var6, byte[] var7) {
 	return langReflectAccess().newField(var1, var2, var3, var4, var5, var6, var7);	//Call newField method through LangReflectAccess
 }

 private static LangReflectAccess langReflectAccess() {	//Get LangReflectAccess
        if (langReflectAccess == null) {
            Modifier.isPublic(1);	//Set access permission public
        }

        return langReflectAccess;
    }


Call the ReflectAccess class to instantiate the Field.

public Field newField(Class<?> declaringClass,
                          String name,
                          Class<?> type,
                          int modifiers,
                          int slot,
                          String signature,
                          byte[] annotations)
    {
        return new Field(declaringClass,
                         name,
                         type,
                         modifiers,
                         slot,
                         signature,
                         annotations);
    }

That is to say, by creating Field, Constructor and Method through the reflection factory, the bottom layer creates a new instance of the corresponding type in the form of new.

5.2Reflection

The Reflection class is similar to a tool class. It provides some local methods and methods used to verify the permissions of related members.

For example:

Local method

@CallerSensitive
public static native Class<?> getCallerClass();//Gets the Class type of the calling Class

Verify member permissions

public static void ensureMemberAccess(Class<?> var0, Class<?> var1, Object var2, int var3) throws IllegalAccessException {
        if (var0 != null && var1 != null) {
            if (!verifyMemberAccess(var0, var1, var2, var3)) {
                throw new IllegalAccessException("Class " + var0.getName() + " can not access a member of class " + var1.getName() + " with modifiers \"" + Modifier.toString(var3) + "\"");
            }
        } else {
            throw new InternalError();
        }
    }

6. Summary

This chapter mainly analyzes the core Class class in java reflection mechanism. The following is a summary:

(1) To operate on a Class or object through reflection, you first need to obtain the Class object of this Class;

(2) There are three ways to obtain class objects: getClass() method, class attribute and class Forname() method;

(3) After getting the Class object, you can get all the information of this Class and carry out subsequent operations;

Keywords: JDK

Added by Cugel on Tue, 01 Feb 2022 15:10:24 +0200