java—— reflection details

Posted by ejames13 on Wed, 08 May 2019 17:18:03 +0200

Reflection is rarely used in our ordinary program development, but it is used extensively in our underlying program design, such as agent mode, factory mode and some other design modes, including the development tools we use, as well as at the bottom of many open source frameworks.So mastering Java's reflection mechanism is very helpful for us to understand the major open source frameworks.

1. Cognitive Reflex

Reflection, from this "negative" word we can see that the normal logic of use is certainly different from our usual, so what is the difference in the end?To understand the concept of "negative", one must first understand the concept of "positive".

Normally, if you want to use a class, you must go through the following steps:

(1) Use important to import the package where the class is located (class: java.lang.Class)

(2) Instantiate class objects by keyword new (construct method: java.lang.reflect.Constructors)

(3) Generating objects can use Object.Attribute to call attributes in the class (Attribute: java.lang.reflect.Field)

(4) Call methods in classes through Object.Method () (method: java.lang.reflect.Method)

The red font in parentheses is the class used in the reflection for each step. If you don't know it now, you can leave it alone. It will be described later. This is for easy comparison.

In reflection, you don't need to import the package of a class to use it; you know all the information in that class by knowing its full path.

Reflection does not require explicit type objects; all objects are represented with Objects.Methods in classes can be called directly with a mixture of Object and reflection mechanisms.

2. Get Class Objects

Once each class is loaded, the system generates a corresponding Class object that provides access to the class in the JVM.There are three ways to obtain Class objects in a java program.
(1) Use the forName(String clazzName) static method of the Class class.
(2) Call the class property of a class to get the corresponding Class object of that class.
(3) Call the getClass() method of an object

2.1class class instantiation object
Class classes that use the forName() method can then operate by calling the newInstance() parameterless constructor method in the Class class, which is defined as follows:

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

Examples are as follows:

class Student {

    public Student() {
        System.out.println("Student Class construction methods");
    }

    @Override
    public String toString() {
        return "Student Class toString Method";
    }
}
public class ReflectDemo {

    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("reflect.Student");
        // Equivalent to keyword instantiation object, Object obj = new Student();
        Object obj = cls.newInstance();
        System.out.println(obj);
    }

}

The output is:

From the example above, you can see that when calling the newInstace() method, the program defaults to calling the parameterless construction method of the Student class, and gets the instantiated object of the Student class, which can call the method properties inside the Student class.

3. Construction method using reflection operation

There are two methods in the Class class to get the construction methods in the class:

Gets all the construction methods in the class:

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

Gets the construction method specified in the class

public Constructor<T> getConstructor(Class<?>... parameterTypes)
        throws NoSuchMethodException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        return getConstructor0(parameterTypes, Member.PUBLIC);
    }

Both methods return the Constructor class from the reflection package (java.lang.reflect. *), which provides information about a single construction method and access to it.

Here's an example of getting all the construction methods in a String class, with the following code:

import java.lang.reflect.Constructor;

public class ReflectStringDemo {
    public static void main(String[] args) throws Exception{
        Class<?> cls = Class.forName("java.lang.String");
        //Get all constructors
        Constructor<?>[] cons = cls.getConstructors();
        //Circular Printing
        for (int i = 0; i < cons.length; i++) {
            System.out.println(cons[i]);
        }
    }
}

Print results:

public java.lang.String(byte[],int,int)
public java.lang.String(byte[],java.nio.charset.Charset)
public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(byte[],int,int,java.nio.charset.Charset)
public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException
public java.lang.String(java.lang.StringBuilder)
public java.lang.String(java.lang.StringBuffer)
public java.lang.String(byte[])
public java.lang.String(int[],int,int)
public java.lang.String()
public java.lang.String(char[])
public java.lang.String(java.lang.String)
public java.lang.String(char[],int,int)
public java.lang.String(byte[],int)
public java.lang.String(byte[],int,int,int)

The example above takes all the construction methods in the String class, including the parameters, exceptions, and so on.

Getting all the construction methods does not seem difficult, and if you want to make a call to the specified construction method, you must focus on the Constructor class and use the newInstance() method for the instance.

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }

Gets the specified parametric construction method and instantiates the object instance:

import java.lang.reflect.Constructor;

class Student2 {
    private String name;

    private Integer age;

    public Student2(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "name:" + this.name + ";age:" + this.age;
    }
}


public class ReflectConstructorDemo {
    public static void main(String[] args) throws Exception {
        Class<?> cls = Class.forName("reflect.Student2");
        Constructor<?> con = cls.getConstructor(String.class, Integer.class);
        // This is equivalent to Object obj = new Student2("Zhang San", 20);
        Object obj = con.newInstance("Zhang San", 20);
        System.out.println(obj);
    }


}

Output results:

As you can see from the above, if you want to instantiate an object, it is much simpler to use a parameterless construction method than a parameterized one. Call the newInstance() method directly with no parameters, get the parameterized construction method first, then use the newInstance() method in the Constructor, and initialize the instance with the specified initialization parameters.Many frameworks use parameterless construction to instantiate objects by default, so parameterless construction is explicitly given in simple Java class development.

Class objects allow you to get a large number of objects, such as Method, Constructor, Field, which represent the methods, constructors, and member variables included in the lock. Programs can also use these objects to perform actual functions, such as calling methods and creating instances.You should consult the API documentation to get started.

4. Create dynamic proxy using Proxy and InvocationHandler

A Proxy class and an InvocationHandler interface are provided under Java's java.lang.reflect package to generate dynamic proxy or dynamic proxy objects.
Proxy provides two methods to create dynamic proxy and dynamic proxy instances.Look at one of the ways:

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException

This method directly creates a dynamic proxy object whose implementation class implements a series of interfaces specified by interfaces, and each method of executing the proxy object is replaced with the invoke method of executing the InvocationHandler object.

Examples are as follows:

interface Person{
    void walk();

    void sayHello(String name);
}

class MyInvokationHandler implements InvocationHandler{

    /**
     * When all methods of a dynamic proxy object are executed, they are replaced with invoke methods as follows.
     * @param proxy: Object representing dynamic proxy
     * @param method: Represents the method being executed
     * @param args: Represents the parameters passed in when the target method is called
     * @return
     * @throws Throwable
     */

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("----Method being executed:" + method);
        if (args != null) {
            System.out.println("The following are the arguments passed in when the method is executed:");
            for (Object val : args) {
                System.out.println(val);
            }
        }else {
            System.out.println("This method was called without arguments");
        }
        return null;
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        //Create an InvocationHandler object
        InvocationHandler handler = new MyInvokationHandler();
        //Generate a dynamic proxy object using the specified InvocationHandler
        Person p = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class[]{Person.class}, handler);
        p.walk();
        p.sayHello("jdfd");
    }
}

The results are as follows:

The result shows that regardless of how the program executes the proxy object, it actually executes the invoke() method of the InvocationHandler object.

Topics: Java Attribute jvm