Don't say much. Go directly to the dry goods Java reflection mechanism principle + practical operation. Uncle Wang, 82, next door, has learned it

Posted by ravi_aptech on Tue, 01 Feb 2022 13:05:40 +0100

Reflection is a computer processing method. The ability of a program to access, detect, and modify its own state or behavior. It can provide objects that encapsulate assemblies and types. (assemblies contain modules, and modules contain types, which in turn contain members.)

reference

1, Overview of reflection

1. Introduction to reflection

  • Reflection is the key to being regarded as a dynamic language. Reflection mechanism allows programs to obtain the internal information of any class with the help of Reflection API during execution, and can directly operate the internal properties and methods of any object.
  • After loading the Class, an object of Class type is generated in the method area of heap memory (a Class has only one Class object), which contains the complete structure information of the Class. We can see the structure of the Class through this object. This object is like a mirror, through which we can see the structure of the Class. Therefore, we call it reflection.

Usual method: introduce the required "package class" name ----- > instantiate through new ----- > to obtain the instantiated object

Reflection method: instantiate the object - > getClass () method - > get the complete "package class" name

Frame = annotation + reflection + design pattern

2. Reflection dynamics

Only when the program is running can we know the calling class

@Test
public void test2(){

    for(int i = 0;i < 100;i++){
        int num = new Random().nextInt(3);//0,1,2
        String classPath = "";
        switch(num){
            case 0:
                classPath = "java.util.Date";
                break;
            case 1:
                classPath = "java.lang.Object";
                break;
            case 2:
                classPath = "com.atguigu.java.Person";
                break;
        }

        try {
            Object obj = getInstance(classPath);
            System.out.println(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/*
    Creates an object of the specified class.
    classPath:Specifies the full class name of the class
     */
public Object getInstance(String classPath) throws Exception {
    Class clazz =  Class.forName(classPath);
    return clazz.newInstance();
}

3. Functions provided by reflection mechanism

  • Determine the class of any object at run time

  • Construct an object of any class at run time

  • Judge the member variables and methods of any class at run time

  • Get generic information at runtime

  • Call the member variables and methods of any object at run time

  • Processing annotations at run time

  • Generate dynamic proxy

Code example

@Test
public void test1() throws Exception {
    Class<Person> clazz = Person.class;
    //1. Create a Person class object through reflection
    Constructor<Person> cons = clazz.getConstructor(String.class, int.class);
    Person person = cons.newInstance("Tom", 12);
    System.out.println(person);//Person{name='Tom', age=12}

    //2. Call the attributes and methods specified by the object through reflection
    //Call properties
    Field age = clazz.getDeclaredField("age");
    age.setAccessible(true);
    age.set(person, 10);
    System.out.println(person.toString());//Person{name='Tom', age=10}

    //Call method
    Method show = clazz.getDeclaredMethod("show");
    show.invoke(person);//my name is Tom and age is 10

    System.out.println("===================================");
    //Through reflection, you can call the private structure of the Person class. For example: private constructors, methods and properties
    //Call private constructor
    Constructor<Person> cons1 = clazz.getDeclaredConstructor(String.class);
    cons1.setAccessible(true);
    Person p1 = cons1.newInstance("Bruce");
    System.out.println(p1);//Person{name='Bruce', age=0}

    //Call private properties
    Field name = clazz.getDeclaredField("name");
    name.setAccessible(true);
    name.set(p1, "Jarry");
    System.out.println(p1);

    //Call private methods
    Method nation = clazz.getDeclaredMethod("nation", String.class);
    nation.setAccessible(true);
    Object nation1 = (String) nation.invoke(p1, "China");//Equivalent to string nation = P1 showNation("China")
    System.out.println(nation1);//I come from China
}

4. Relevant API

  • java.lang.Class: source of reflection
  • java.lang.reflect.Method: reflection method
  • java.lang.reflect.Field: reflection parameter
  • java.lang.reflect.Constructor: Reflection constructor
  • ...

2, Class class

1. Class description

  • The following methods are defined in the Object class, which will be inherited by all subclasses:

public final Class getClass()

  • The type of the return value of the above method is a Class class, which is the source of Java reflection. In fact, the so-called reflection is also well understood from the running results of the program, that is, the name of the Class can be obtained through object reflection.

  • The information that can be obtained after the object uses reflection: the properties, methods and constructors of a Class, and which interfaces a Class implements. For each Class, the JRE keeps an object of the same Class type for it. A Class object contains information about a specific structure (class/interface/enum/annotation/primitive type/void / []).

    • Class itself is also a class
    • Class objects can only be created by the system
    • A loaded Class will only have one Class instance in the JVM
    • A class object corresponds to one loaded into the JVM Class file
    • Each Class instance will remember which Class instance it was generated from
    • A complete Class structure can be loaded through all classes
    • Class is the root of Reflection. For any class you want to dynamically load and run, you have to obtain the corresponding class object first

Loading process of class:

  • The program passes javac Exe command will generate one or more bytecode files (. End of Class). Then we use Java Exe command to interpret and run a bytecode file. It is equivalent to loading a bytecode file into memory. This process is called Class loading. The Class loaded into memory is called the runtime Class. This runtime Class is used as an instance of Class.
  • In other words, an instance of Class corresponds to a runtime Class.
  • The runtime classes loaded into memory will be cached for a certain period of time. Within this time, we can get this runtime class in different ways.

2. Common methods of class

Code example:

//Create an instance of Class
String str = "test1.Person";
Class clazz = Class.forName(str);
//Call the empty parameter constructor of Class to create the object
Object obj = clazz.newInstance;
//Get the name attribute of clazz
Field field = clazz.getField("name");
field.set(obj,"Jarry");

Object name = filed.get(obj);
System.out.println(name);
//test1.Person is the person class under test1 package

3. Several ways to obtain Class instances:

1) Given a specific class, this method is the most safe and reliable and has the highest program performance. Example: class clazz = string class;

2) Given an instance of a Class, call the getClass () method of the instance to obtain the Class object instance: Class clazz = person getclass();

3) If the full Class name of a Class is known and the Class is under the Class path, it can be obtained through the static method forName() of Class class, and ClassNotFoundException (more commonly used) may be thrown

Example: class clazz = class forName(String classPath)

4) Through classloader CL = this getclass(). getClassLoader(); Class clazz = cl.loadclass ("full class name of class");

Code example

@Test
public void test2() throws ClassNotFoundException {
    //Method 1: call the properties of the runtime class: class
    Class<Person> clazz1 = Person.class;
    System.out.println(clazz1);//class cn.bruce.java.Person

    //Method 2: call getClass() through the object of the runtime class
    Person p1 = new Person();
    Class<? extends Person> clazz2 = p1.getClass();
    System.out.println(clazz2);//class cn.bruce.java.Person

    //Method 3: call the static method of Class: forName(String classPath)
    Class<?> clazz3 = Class.forName("cn.bruce.java.Person");
    System.out.println(clazz3);//class cn.bruce.java.Person

    System.out.println(clazz1 == clazz2);//true
    System.out.println(clazz1 == clazz3);//true
    //Method 4: use class loader: ClassLoader (understand)
    ClassLoader classLoader = ReflectionTest.class.getClassLoader();
    Class<?> clazz4 = classLoader.loadClass("cn.bruce.java.Person");
    System.out.println(clazz4);//class cn.bruce.java.Person
    System.out.println(clazz1 == clazz4);//true
}

Summary: how to create class objects?

Mode 1: new + constructor

Method 2: to create an object of class Xxx, you can consider checking whether there are static methods in classes Xxx, Xxxs, XxxFactory and XxxBuilder. You can call its static method to create an Xxx object.

Mode 3: through reflection

4. The structure that a class instance can represent

(1) Class: external class, member (member internal class, static internal class), local internal class, anonymous internal class

(2) interface: interface

(3) []: array

(4) enum: Enumeration

(5) Annotation: annotation @ interface

(6) primitive type: basic data type

(7) void: no return value

In Java, everything is an object

Code example

@Test
public void test3(){
    Class<Object> c1 = Object.class;
    Class<Comparable> c2 = Comparable.class;
    Class<String[]> c3 = String[].class;
    Class<int[][]> c4 = int[][].class;
    Class<ElementType> c5 = ElementType.class;
    Class<Override> c6 = Override.class;
    Class<Integer> c7 = int.class;
    Class<Void> c8 = void.class;
    Class<Class> c9 = Class.class;

    int[] i1 = new int[10];
    int[] i2 = new int[100];
    Class<? extends int[]> c10 = i1.getClass();
    Class<? extends int[]> c11 = i2.getClass();
    // As long as the element type of the array is the same as the dimension, it is the same Class
    System.out.println(c10 == c11);//true
}

3, Class loading

1. Class loading process

When a program actively uses a class, if the class has not been loaded into memory, the system will initialize the class through the following three steps.

  • Load: load the bytecode content of the class file into memory, convert these static data into the runtime data structure of the method area, and then generate a Java. XML file representing this class Lang. class object, as the access entry (i.e. reference address) of class data in the method area. All class data that needs to be accessed and used can only be accessed through this class object. This loading process requires the participation of the class loader.
  • Link: the process of merging the binary code of Java classes into the running state of the JVM.
    • Verification: ensure that the loaded class information complies with the JVM specification. For example, starting with cafe, there is no security problem.
    • Preparation: the stage of formally allocating memory for class variables (static) and setting the default initial value of class variables. These memory will be allocated in the method area.
    • Resolution: the process of replacing the symbolic reference (constant name) in the virtual machine constant pool with a direct reference (address).
  • initialization:
    • The process of executing the < clinit > () method of the class constructor. The class constructor < clinit > () method is generated by the combination of the assignment actions of all class variables in the class and the statements in the static code block automatically collected at compile time. (the class constructor is the constructor that constructs class information, not the constructor that constructs the class object.).
    • When initializing a class, if you find that its parent class has not been initialized, you need to trigger the initialization of its parent class first.
    • Virtual opportunity ensures that the < clinit > () method of a class is locked and synchronized correctly in a multithreaded environment.

Code example:

public class ClassLoadingTest{
    public static void main (String [] args){
        System.out.println(test.m);
    }
}

class test{
    static {
        m = 300;
    }
    static int m = 100;
}
//Step 1: load
//Step 2: after the link is completed, m=0
//Step 3: after initialization, the value of m is determined by the < clinit > () method
/*
This test constructor < clinit > () method is generated by combining the assignment of class variables and the statements in the static code block in order, similar to
<clinit>(){
m = 300;
m = 100;
}
*/

2. The process of compiling, running and executing Java classes

3. Function of class loader

  • Load the bytecode content of the class file into memory, convert these static data into the run-time data structure of the method area, and then generate a Java. Net file representing this class in the heap Lang. class object, as the access entry of class data in the method area.

  • Class caching: the standard Java se class loader can find classes as required, but once a class is loaded into the class loader, it will remain loaded (cached) for a period of time. However, the JVM garbage collection mechanism can recycle these class objects

4. Classification of class loaders

@Test
public void test1(){
    //For custom classes, use the system class loader to load them
    ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
    System.out.println(classLoader);
    //Call getParent() of system class loader: get extended class loader
    ClassLoader classLoader1 = classLoader.getParent();
    System.out.println(classLoader1);
    //Calling getParent() of extension class loader: unable to get boot class loader
    //The boot class loader is mainly responsible for loading the core class library of java, and cannot load the custom class.
    ClassLoader classLoader2 = classLoader1.getParent();
    System.out.println(classLoader2);

    ClassLoader classLoader3 = String.class.getClassLoader();
    System.out.println(classLoader3);

}

5. Use Classloader to load the configuration file under src directory

@Test
public void test3(){
    Properties pros = new Properties();
    //        //Method 1 of reading configuration file:
    //        //At this time, the file is under the current module by default.
    //        FileInputStream fis = null;
    //        try {
    //            fis = new FileInputStream("jdbc1.properties");
    //            pros.load(fis);
    //        } catch (IOException e) {
    //            e.printStackTrace();
    //        } finally {
    //            if (fis != null) {
    //                try {
    //                    fis.close();
    //                } catch (IOException e) {
    //                    e.printStackTrace();
    //                }
    //            }
    //        }

    //The second way to read the configuration file is to use ClassLoader
    //The default identification of the configuration file is: under src of the current module
    ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
    InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
    try {
        pros.load(is);
    } catch (IOException e) {
        e.printStackTrace();
    }

    String user = pros.getProperty("user");
    String password = pros.getProperty("password");
    System.out.println("user = " + user + " password =" + password);
}

4, Application of reflection

1. Create the object of the runtime class

1.1 code examples

@Test
public void test1() throws Exception {
    //Mode 1
    Class<Person> clazz1 = Person.class;
    //Mode 2
    Class<Person> clazz2 = (Class<Person>) Class.forName("cn.bruce.java.Person");

    Person person1 = clazz1.newInstance();
    Person person2 = clazz2.newInstance();
    System.out.println(person1);
    System.out.println(person2);

}

1.2 description

newInstance(): call this method to create the object of the corresponding runtime class. Constructor that internally calls an empty parameter of the runtime class.

To create the object of the runtime class normally with this method, you need to:

  • The runtime class must provide a constructor with null parameters

  • Empty argument constructor has sufficient access rights. Usually, it is set to public.

A public null parameter constructor is required in the javabean. reason:

  • It is convenient to create objects of runtime classes through reflection

  • When a subclass inherits this runtime class, it is ensured that the parent class is the constructor when super() is called by default

2. Get the complete structure of the runtime class

Through reflection, we can obtain all properties, methods, constructors, parent classes, interfaces, generics, packages, annotations, exceptions, etc. of the corresponding runtime class....

2.1 using reflection can obtain:

  1. All interfaces implemented: public class <? > [] getinterfaces() determines the interface implemented by the class or interface represented by this object.

  2. Inherited parent Class: public Class <? Super T > getsuperclass() returns the Class representing the parent Class of the entity (Class, interface, basic type) represented by this Class.

  3. All constructors:

    public Constructor<T>[] getConstructors()

    Returns all public constructor methods of the Class represented by this Class object.

    public Constructor<T>[] getDeclaredConstructors()

    The Class of the constructed object represents all the methods returned by this Class.

    In the Constructor class:

    • Get modifiers: public int getModifiers();
    • Get method name: public String getName();
    • Get the type of parameter: public class <? > getParameterTypes();
  4. All methods:

    public Method[] getDeclaredMethods()

    Returns all methods of the Class or interface represented by this Class object

    public Method[] getMethods()

    Returns the public method of the Class or interface represented by this Class object

    Method class:

    • public Class<?> Getreturntype(): get all return values
    • public Class<?> [] getparametertypes(): get all parameters
    • public int getModifiers(): get modifiers
    • public Class<?> [] getexceptiontypes(): get exception information
  5. All fields:

    public Field[] getFields()

    Returns the public Field of the Class or interface represented by this Class object.

    public Field[] getDeclaredFields()

    Returns all fields of the Class or interface represented by this Class object

    In Field method

    • public int getModifiers(): returns the modifier of this Field as an integer
    • public Class<?> GetType (): get the attribute type of Field
    • public String getName(): returns the name of the Field.
  6. Annotation related

    get Annotation(Class<T> annotationClass)

    getDeclaredAnnotations()

  7. Generic correlation

    Get generic type of parent class: Type getGenericSuperclass()

    Generic type: parametrizedtype

    Get the actual generic type parameter array: getActualTypeArguments()

  8. Package of class getpackage()

2.2 code example

Get property fielded

public class FiledTest {
    @Test
    public void test1() {
        Class<Person> clazz = Person.class;
        //Get attribute structure
        //getFields(): get the attributes declared as public access rights in the current runtime class and its parent class
        Field[] fields = clazz.getFields();
        for (Field f :
             fields) {
            System.out.println(f);
        }
        System.out.println();
        //Getdeclaraedfields(): get all the attributes declared in the current runtime class. (excluding properties declared in the parent class)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f :
             declaredFields) {
            System.out.println(f);
        }
    }

    //Permission modifier data type variable name
    @Test
    public void test2() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("cn.bruce.java1.Person");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f :
             declaredFields) {
            //1. Permission modifier
            int modifiers = f.getModifiers();
            System.out.print(Modifier.toString(modifiers)+"\t");

            //2. Data type
            Class<?> type = f.getType();
            System.out.print(type.getName()+"\t");

            //3. Variable name
            String fName = f.getName();
            System.out.print(fName);

            System.out.println();
        }
    }
}

Get Method

public class MethodTest {
    @Test
    public void test1() {
        Class<Person> clazz = Person.class;
        //getMethods(): get the method declared as public permission in the current runtime class and all its parent classes
        Method[] methods = clazz.getMethods();
        for (Method m :
             methods) {
            System.out.println(m);
        }
        System.out.println("============");
        //Getdeclaraedmethods(): get all the methods declared in the current runtime class. (excluding methods declared in the parent class)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m :
             declaredMethods) {
            System.out.println(m);
        }
    }

    /*
    @Xxxx
    Permission modifier return value type method name (parameter type 1, parameter name 1,...) throws XxxException{}
     */
    @Test
    public void test2() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("cn.bruce.java1.Person");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m :
             declaredMethods) {
            //1. Get the annotation of the method declaration
            Annotation[] annos = m.getAnnotations();
            for (Annotation a :
                 annos) {
                System.out.println(a);
            }

            //2. Permission modifier
            System.out.print(Modifier.toString(m.getModifiers())+"\t");

            //3. Return value type
            System.out.print(m.getReturnType().getName() + "\t");

            //4. Method name
            System.out.print(m.getName());
            System.out.print("(");

            //5. Formal parameter list
            Class<?>[] parameterTypes = m.getParameterTypes();
            if (!(parameterTypes == null && parameterTypes.length == 0)) {
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i == parameterTypes.length - 1) {
                        System.out.print(parameterTypes[i].getName() + " args_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i].getName() + "args_" + i + ",");
                }
            }
            System.out.print(")");

            //6. Exceptions thrown
            Class<?>[] exceptionTypes = m.getExceptionTypes();
            if (exceptionTypes.length > 0){
                System.out.print("throws ");
                for (int i = 0; i < exceptionTypes.length; i++) {
                    if (i==exceptionTypes.length -1){
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName()+",");
                }

                System.out.println();
            }

        }
    }
}

Get other structures

public class OtherTest {
    /*
    Get constructor structure
     */
    @Test
    public void test1() {
        Class<Person> clazz = Person.class;
        //getConstructors(): get the constructor declared as public in the current runtime class
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor c :
             constructors) {
            System.out.println(c);
        }
        System.out.println("================");
        //Getdeclaraedconstructors(): get all constructors declared in the current runtime class
        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor c :
             declaredConstructors) {
            System.out.println(c);
        }
    }
    /*
    Gets the parent class of the runtime class
     */
    @Test
    public void test2(){
        Class<Person> clazz = Person.class;

        Class<? super Person> superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }
    /*
    Gets the generic parent class of the runtime class
     */
    @Test
    public void test3(){
        Class<Person> clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
    }
    /*
    Gets the generic type of the generic parent class of the runtime class
    Code: logical code vs functional code
     */
    @Test
    public void test4(){
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        //Get generic type
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
        //        System.out.println(actualTypeArguments[0].getTypeName());
        System.out.println(((Class)actualTypeArguments[0]).getName());
    }

    /*
    Gets the interface implemented by the runtime class
     */
    @Test
    public void test5(){
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
            System.out.println(c);
        }

        System.out.println();
        //Gets the interface implemented by the parent class of the runtime class
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c : interfaces1){
            System.out.println(c);
        }

    }
    /*
        Gets the package where the runtime class is located
     */
    @Test
    public void test6(){
        Class clazz = Person.class;

        Package pack = clazz.getPackage();
        System.out.println(pack);
    }

    /*
        Gets the annotation of the runtime class declaration
     */
    @Test
    public void test7(){
        Class clazz = Person.class;

        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation annos : annotations){
            System.out.println(annos);
        }
    }
}

3. Call the specified structure of the runtime class

3.1 call the specified attribute

In the reflection mechanism, the attributes in the class can be operated directly through the Field class, and the operation of setting and obtaining the attribute content can be completed through the set() and get() methods provided by the Field class.

  • public Field getField(String name) returns the specified public Field of the Class or interface represented by this Class object.

  • Public Field getdeclaraedfield (string name) returns the specified Field of the Class or interface represented by this Class object.

    In Field:

  • public Object get(object obj) gets the attribute content of this Field on the specified object obj

  • public void set(Object obj,Object value) sets the attribute content of this Field on the specified object obj

Code example

@Test
public void testField() throws Exception {
    Class clazz = Person.class;

    //Create an object of a runtime class
    Person p = (Person) clazz.newInstance();

    //1\.  Getdeclaraedfield (string fieldname): get the attribute of the specified variable name in the runtime class
    Field name = clazz.getDeclaredField("name");

    //2. Ensure that the current attribute is accessible
    name.setAccessible(true);
    //3. Get and set the property value of the specified object
    name.set(p,"Tom");

    System.out.println(name.get(p));
}

3.2 call the specified method (common)

Through reflection, the Method in the class is called and completed through the Method class. Steps:

  1. Get a Method object through the getMethod(String name,Class... parameterTypes) Method of Class class, and set the parameter type required for the operation of this Method.

  2. After invoking the obj [, use the obj [, Vos] method to set the information to be passed to the obj [, Vos].

Object invoke (object, object... args) method:

  1. Object corresponds to the return value of the original method. If the original method has no return value, null is returned

  2. If the original method is a static method, the formal parameter Object obj can be null at this time

  3. If the original method parameter list is empty, Object[] args is null

  4. If the original method is declared as private, you need to explicitly call the setAccessible(true) method of the method object before calling the invoke() method to access the private method.

About the use of setAccessible method:

  • Method, Field and Constructor objects all have setAccessible() methods.

  • setAccessible is a switch that enables and disables access security checks

  • If the parameter value is true, it indicates that the Java language access check should be canceled when the reflected object is used.

  • Improve the efficiency of reflection. If reflection must be used in the code, and the code of this sentence needs to be called frequently, please set it to true So that private members that cannot be accessed can also be accessed

  • If the parameter value is false, it indicates that the reflected object should implement Java language access check.

Code example

@Test
public void testMethod() throws Exception {
    Class<Person> clazz = Person.class;

    //Create an object of a runtime class
    Person person = clazz.newInstance();

    /*
        1.Gets a specified method
        getDeclaredMethod():Parameter 1: indicates the name of the obtained method parameter 2: indicates the formal parameter list of the obtained method
         */
    Method show = clazz.getDeclaredMethod("show", String.class);

    //2. Ensure that the current method is accessible
    show.setAccessible(true);

    /*
        3\. invoke() of the calling method: parameter 1: caller of the method parameter 2: argument that assigns value to the method parameter
        invoke()The return value is the return value of the method called in the corresponding class.
         */
    Object returnValue = show.invoke(person, "CHN");
    System.out.println(returnValue);

    System.out.println("*************How to call static methods*****************");

    Method showDesc = clazz.getDeclaredMethod("showDesc");
    showDesc.setAccessible(true);
    //This invoke() returns null if the method in the called runtime class does not return a value
    //Object returnVal = showDesc.invoke(null);
    Object returnVal = showDesc.invoke(Person.class);
    System.out.println(returnVal);
}

3.3 call the specified constructor

Code example

@Test
public void testConstructor() throws Exception {
    Class clazz = Person.class;

    //private Person(String name)
    /*
        1.Gets the specified constructor
        getDeclaredConstructor():Parameter: indicates the parameter list of the constructor
         */

    Constructor constructor = clazz.getDeclaredConstructor(String.class);

    //2. Make sure this constructor is accessible
    constructor.setAccessible(true);

    //3. Call this constructor to create the object of the runtime class
    Person per = (Person) constructor.newInstance("Tom");
    System.out.println(per);

}

4. Dynamic agent

4.1 principle of agent design mode:

Wrap the object with a proxy object and replace the original object with the proxy object. Any call to the original object must pass through the proxy. The proxy object determines whether and when to transfer the method call to the original object.

4.2 static agent

Code example:

interface ClothFactory{
    void produceCloth();
}

//Proxy class
class NikeClothFactory implements ClothFactory{

    @Override
    public void produceCloth() {
        System.out.println("Nike Produce clothes");
    }
}

//proxy class
class ProxyClothFactory implements ClothFactory{

    private ClothFactory factory;//Instantiate with the proxied class object

    public ProxyClothFactory(ClothFactory factory) {
        this.factory = factory;
    }

    @Override
    public void produceCloth() {
        System.out.println("Agent the factory to do some preparatory work");

        factory.produceCloth();

        System.out.println("Agent the factory to do some follow-up closing work");

    }
}

//test
public class StaticProxyTest {
    public static void main(String[] args) {

        //Create the object of the proxied class
        ClothFactory nike = new NikeClothFactory();

        //Create object of proxy class
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike);

        proxyClothFactory.produceCloth();
    }
}

Disadvantages of static proxy:

① The classes of proxy class and target object are determined during compilation, which is not conducive to program expansion.

② Each agent class can only serve one interface, so there must be too many agents in program development.

4.3 characteristics of dynamic agent:

Dynamic proxy refers to the method that the client calls other objects through the proxy class, and it is the proxy object that dynamically creates the target class when the program runs.

Advantages over static agents:

All methods declared in the abstract role (Interface) are transferred to a centralized method of the calling processor. In this way, we can deal with many methods more flexibly and uniformly.

4.4 implementation of dynamic agent

4.4.1 two main problems to be solved:

Question 1: how to dynamically create a proxy class and its object according to the proxy class loaded into memory.

(implemented through Proxy.newProxyInstance())

Question 2: when method a is called through the object of the proxy class, how to dynamically call the method with the same name in the proxy class.

(through the implementation class of InvocationHandler interface and its method invoke())

4.4.2 API related to dynamic agent:

Proxy: the operation class that is dedicated to proxy. It is the parent class of all dynamic proxy classes. This class dynamically generates implementation classes for one or more interfaces. Provides static methods for creating dynamic proxy classes and dynamic proxy objects.

  • static Class<?> Getproxyclass (classloader, loader, class <? >... Interface) creates a class object corresponding to a dynamic proxy class
  • Static object newproxyinstance (classloader, loader, class <? >... Interface, invocationhandler h) directly creates a dynamic proxy object
4.4.3 implementation steps of dynamic agent:
  1. Create a class that implements the interface InvocationHandler. It must implement the invoke method to complete the specific operation of the agent.

  2. Create proxied classes and interfaces

  3. Create an interface Proxy through the Proxy's static method newproxyinstance (classloader, loader, class <? >... Interface, invocationhandler h)

  4. Call the method of the proxy class through the instance of the proxy class

4.4.2 code implementation:
interface Human {
    String getBelief();

    void eat(String food);
}

//Proxy class
class SuperMan implements Human {

    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }

    @Override
    public void eat(String food) {
        System.out.println("I like eat " + food);
    }
}

/*
What problems need to be solved in order to realize dynamic agent?
Question 1: how to dynamically create a proxy class and its object according to the proxy class loaded into memory.
Question 2: when method a is called through the object of the proxy class, how to dynamically call method a with the same name in the proxy class.
 */

//Create a class that inherits the InvocationHandler interface
class MyInvocationHanlder implements InvocationHandler {
    private Object obj;//You need to use the object of the proxy class for assignment

    public void bind(Object obj) {
        this.obj = obj;
    }
    //When we call method a through the object of proxy class, we will automatically call the following method: invoke()
    //The function of method a to be executed by the proxy class is declared in invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //Method: that is, the method called for the proxy class object, which is also the method to be called by the proxy class object
        //obj: object of the proxied class
        Object returnValue = method.invoke(obj, args);

        //The return value of the above method is the return value of invoke() in the current class.
        return returnValue;
    }
}

class ProxyFactory {
    //Call this method to return an object of a proxy class. Solve problem 1
    public static Object getProxyInstance(Object obj) {
        MyInvocationHanlder hanlder = new MyInvocationHanlder();
        hanlder.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),hanlder);

    }
}

//Test dynamic agent
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance: object of proxy class
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //When a method is called through a proxy class object, it will automatically call the method with the same name in the proxy class
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("Hot Pot");
    }
}

5. Dynamic agent and AOP

  • When using Proxy to generate a dynamic Proxy, it often does not generate a dynamic Proxy out of thin air, which is of little significance. Usually, dynamic Proxy is generated for the specified target object.

  • This dynamic agent is called AOP agent in AOP. AOP agent can replace the target object. AOP agent includes all methods of the target object. However, there are differences between the methods in AOP agent and target object:

  • The method in AOP agent can insert some general processing before and after the execution of the target method

Code example

//Common interface
interface Dog {
    void info();

    void run();
}

//Proxy class
class HuntingDog implements Dog {

    @Override
    public void info() {
        System.out.println("I'm a hound");
    }

    @Override
    public void run() {
        System.out.println("I run fast");
    }
}

//General method
class DogUtils {
    public void method1() {
        System.out.println("=======General method I=======");
    }

    public void method2() {
        System.out.println("=======General method II=======");
    }
}

//Dynamic agent implementation
class MyInvocationHandler1 implements InvocationHandler {
    //Object to be represented
    private Object target;

    public void SetTarget(Object target) {
        this.target = target;
    }

    //When we call method a through the object of proxy class, we will automatically call the following method: invoke()
    //The function of method a to be executed by the proxy class is declared in invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        DogUtils dogUtils = new DogUtils();
        //Execute method1 in the DogUtils object
        dogUtils.method1();
        //The execution method method is called through the obj object
        Object result = method.invoke(target, args);
        //Execute method2 in the DogUtils object
        dogUtils.method2();
        return result;
    }
}

//Dynamic proxy class
class MyProxyFactory1 {
    //Generate dynamic proxy object for target
    public static Object getProxy(Object target) {
        //Create a MyInvocationHandler object
        MyInvocationHandler1 handler = new MyInvocationHandler1();
        //Set the target object for MyInvocationHandler
        handler.SetTarget(target);
        //Create and return a dynamic proxy object
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
    }
}

public class AOPTest {
    public static void main(String[] args) {
        Dog target = new HuntingDog();
        Dog dog = (Dog) MyProxyFactory1.getProxy(target);
        dog.info();
        dog.run();
    }
}

Topics: Java reflection