God perspective JAVA - Foundation 19 - reflection, dynamic agent [2021-09-10]

Posted by jimdidr on Sat, 20 Nov 2021 13:27:43 +0100

1. Reflection

Reflection is the key to the magic of JAVA.

Reflection mechanism allows the program 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 type Class is generated in the method area of heap memory. All classes we define are Class objects.

A Class has only one Class object. The objects of the Class we define are objects belonging to Class objects.

This Class object contains the complete result information of the Class. You can get the structure of the Class by getting this object. This object is like a mirror. Seeing the structure of the Class through this mirror is called reflection.

The process of obtaining the structure of a class through reflection:

Instantiate the object - > getClass method to obtain the bytecode file - > obtain the completed structure

Do you remember the java running process at the beginning?

 

Compile the. java file through javac.exe to get the. class file.

The getClass() method is to get the. Class file. What kind of file is this? See the following. The following is a commonly used class Animal.java at the beginning, and the Animal.class file generated through javac.exe.

  

You can see that this is a binary file. This is opened using the sublim editor. We can't understand these binaries at all. But it doesn't matter. The program can understand. This file contains all the information of the Animal class.

What can you do with reflection?

During operation, you can do the following things, including but not limited to:

  • Determine the class to which any object belongs

  • Construct an object of any class

  • Judge the member variables and methods of any class

  • Get generic information

  • Call the member variables and methods of any object

  • Processing annotations

  • Generate dynamic proxy

1.1 main API

java.lang.Class class

This is a class, which is capitalized, and the keyword is lowercase. java is strictly case sensitive.

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

Class is used to describe a general class. Above is the class header. You can see that this class implements four interfaces.

java.lang.reflect.Method class: a class that describes the methods of a class

public final class Method extends Executable {}

The java.lang.reflect.Field class describes the class of class member variables

public final
class Field extends AccessibleObject implements Member {}

The java.lang.reflect.Constructor class describes the class of the constructor of the class

public final class Constructor<T> extends Executable {}

wait.

Except that Class class is under lang package, all other classes are under reflect package, a sub package of lang package. These classes are basically defined as final. That is, it cannot be inherited.

public class Person {
     // Private property
    private String name;
    public int age;
​
    public void show(){
        System.out.println("Public method: This is Person Class show Method printed");
    }
     // Private method
    private String showNational(String str){
        System.out.println("Private method: This is Person Class showNational Method printed:" + str);
        return str;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
​
    public void setAge(int age) {
        this.age = age;
    }
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // Private constructor
    private Person(String name) {
        this.name = name;
    }
}

There is such a Person class as above. Let's try how to use reflection

// We can do these things without reflection
// 1. Create an object of the Person class
Person p1 = new Person("Tom", 12);
// 2. Call public properties and methods through objects.
p1.age = 10;
System.out.println(p1);
p1.show();
// Outside the Person class, private structures cannot be called through objects. Including private properties, methods, constructors.

Use reflection to do the same thing as above.

// Try using reflection
// The Person class has an attribute class, which is an object of the class class.
Class clazz = Person.class;
// Get constructor
Constructor cons = clazz.getConstructor(String.class, int.class);
// create object
Object tom = cons.newInstance("Tom", 12);
// Strong rotation
Person p = (Person) tom;
System.out.println(tom.toString());
// Calling properties and methods through reflection
// Call properties
Field age = clazz.getDeclaredField("age");
age.set(p, 10);
// Call the show method
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);

In addition to the above functions, reflection can also call private properties, methods and constructors that cannot be called in ordinary ways.

If reflection can only do things in the same way as ordinary ways, there is no need for reflection.

// The Person class has an attribute class, which is an object of the class class.
Class clazz = Person.class;
​
// Calling a private constructor using a clazz object
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
// Set the private that can be accessed
cons1.setAccessible(true);
// create object
Object obj = cons1.newInstance("Jerry");
// Strong turn back to Person object
Person jerry = (Person) obj;
// Call private property
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(jerry, "jerry_big");
System.out.println(jerry);
// Call private method
Method showNational = clazz.getDeclaredMethod("showNational", String.class);
showNational.setAccessible(true);
String str = (String) showNational.invoke(jerry, "Reflection call private hahaha");
System.out.println(str); 
​
// output
// Person{name='jerry_big', age=0}
// Private method: This is printed by the showNational method of the Person class: Reflection calls private hahaha
// Reflection call private hahaha

As can be seen from the above code, it is mainly obtained through the class name point class. Whether private or shared, it can be obtained. However, if it is private, it is necessary to call setAccessible method to obtain permission. When a method with a return value is called, the return value of the invoke method is the return value of the original method.

1.2 thinking

1. Reflection can call private things. Then, in object-oriented, the encapsulation of classes, that is, permissions. Does it fail? Will this be contradictory?

There is no contradiction. These two requirements are needed in our actual development. Private is to tell you not to use it. Common is to tell you that it is provided for you. But it must be used, which is also possible.

2. Reflection can do the same things as ordinary calls. Is it unnecessary to use ordinary calls?

In terms of code quantity, there are obviously fewer ordinary calls and clearer logic. There is no trouble of coercion. Reflection is more about providing a dynamic.

When ordinary calls cannot meet the requirements, such as the runtime, it knows what to call. Then you need to use reflection.

1.3 Class

In the code of 1.1, reflection is basically operated through Class objects.

It is necessary to study this Class.

When a class is compiled into a. Class bytecode file. Use java.exe to interpret and run the bytecode file. It is equivalent to loading the bytecode file into memory. This process is class loading.

The classes loaded into memory are called runtime classes. This runtime Class is the object of Class.

Class clazz = Person.class;

We get the object of class class by class name. Class. In fact, the class name point class represents the class itself. But you can't write the class name directly, otherwise it's a type of code. In order to distinguish from types, add an attribute class to each class, and use the class name point class to represent the bytecode file. When loaded into memory, it becomes a class object.

There are more ways to get objects of Class than the above. And Class has generics

 Class<Person> clazz = Person.class;
// Method 2: call the getClass method through the object of the runtime class
Person p1 = new Person();
Class aClass = p1.getClass();

Method 3: use the static method forName of Class. The full Class name needs to be passed in to prevent name conflicts

Class clazz = Class.forName("com.xgzit.JavaBaseStudy.reflectStudy.Person");

Method 4: use class loader

// First, get the object of ClassLoader class through the current class 
// Then use this object to load the Class to be obtained, and you will get the Class object.
ClassLoader classLoader = TestMain.class.getClassLoader();
Class clazz = classLoader.loadClass("com.xgzit.JavaBaseStudy.reflectStudy.Person");

The runtime Class loaded into memory will be cached for a certain time. No matter how many methods are used, the objects of the same Class will be obtained.

Just like there is only one in Beijing, you can get to Beijing by different means of transportation

Of the above four methods, mode 3 is the most used. Because mode 1 is dead. If it is a class that does not exist at present, the compilation will report an error, which is not flexible enough.

There are objects in mode 2. Generally, there is no reflection. Method 3 is flexible. Even if the full class name does not exist at present, no error will be reported. Only when it does not exist at runtime will an error be reported.

java does not only have classes, but also interfaces, arrays, enumerations, annotations, basic data types, etc. are these loaded as objects of Class class?

In real time. void actually counts.

 

Class c1 =  Object.class;
Class c2 =  Comparable.class;
Class c3 =  String[].class;
Class c4 =  int[][].class;
Class c5 =  ElementType.class;
Class c6 =  Override.class;
Class c7 =  int.class;
Class c8 =  void.class;
Class c9 =  Class.class;

The above nine examples are correct.

int[] a = new int[10];
int[] b = new int[100];
Class c1 = a.getClass();
Class c2 = b.getClass();
System.out.println(c1 == c2); // true

The above code shows that as long as the dimensions and types of arrays are consistent, such as 1-dimensional arrays of int type, their Class objects are the same.

A loading process of class:

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.

 

Class loading and ClassLoader

 

The function of class loading: load the contents of class bytecode file into memory, convert these data into the runtime data structure of the method area, and then generate a class object representing this class in the heap as the access entry of class data in the method area.

Class caching: the standard javaSe class loader can find classes as required, but once a class is loaded into the class loader, it will maintain loading (caching) for a period of time, but the JVM garbage collection mechanism can recycle these class objects.

There are four types of class loaders.

 

The boot class is responsible for the core library, the extension class is to load the imported jar package, and the system class is to load the classes written by ourselves. Customized ones are not introduced here.

// The class loader that gets the custom class is the system class loader
ClassLoader classLoader = TestMain.class.getClassLoader();
System.out.println(classLoader);
// Gets that the parent loader of the system class loader is an extension class loader
ClassLoader parent = classLoader.getParent();
System.out.println(parent);
// Get the parent loader of the extension class is the boot class loader, which cannot be obtained directly!!!
ClassLoader parentParent = parent.getParent();
System.out.println(parentParent);
// output
// sun.misc.Launcher$AppClassLoader@18b4aac2
// sun.misc.Launcher$ExtClassLoader@4fca772d
// null

The String class is the core library, which is loaded by the boot class loader. You can't get the class loader of String class directly.

1.4 loading configuration files

// The characteristic of properties is that the key value is String
Properties pros = new Properties();
File file = new File("jdbc.properties");
// The files in idea are under the current Module by default
FileInputStream fis = new FileInputStream(file);
pros.load(fis);
​
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println(name);

The above code is the way to load the configuration file mentioned earlier.

Using ClassLoader can also be used to load configuration files

// The characteristic of properties is that the key value is String
ClassLoader loader = TestMain.class.getClassLoader();
InputStream stream = loader.getResourceAsStream("jdbc1.properties");
Properties properties = new Properties();
properties.load(stream);
​
String name = properties.getProperty("name");
System.out.println(name);

The classLoader object calls the getResourceAsStream method. The default file is in the src directory of the current module. But I failed the test. I don't know what the hell

1.5 creating objects of runtime classes

The class loaded into memory is the runtime class.

Class<Person> clazz = Person.class;
// Create the object of the corresponding runtime class
Person o = clazz.newInstance();
System.out.println(o); // Person{name='null', age=0}

First, you need to get the Class object of the corresponding Class. Call the newInstance method through the Class object. Create an object.

newInstance is actually an empty argument constructor for the call. Therefore, an empty parameter constructor must be provided. Another problem is permissions.

If the null parameter constructor permission is private, it cannot be called, and an illegal access exception will be reported. Usually, the permission is public

  • The object that creates the runtime class needs to provide an empty parameter constructor for this class

  • The permission of this null parameter constructor is generally public

In order to write general-purpose code, you need to provide an empty parameter constructor. In fact, you can get the parameterized constructor through getdeclaraedconstructor to create objects.

However, the parameter constructors of each class may have different parameters, so it is difficult to write general code, while the empty parameter constructors are the same without parameters. If there is an attribute, it will be added later in the form of attribute assignment.

   
 public static void main(String[] args) throws Exception{
      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.xgzit.JavaBaseStudy.reflectStudy.Person";
                break;
            default:
                break;
        }
        Object instance = getInstance(classPath);
        System.out.println(instance);
    }
    // Get object
    public static Object getInstance(String classPath) throws Exception{
        Class<?> aClass = Class.forName(classPath);
        return aClass.newInstance();
    }

The above code reflects the dynamic nature of reflection. Compile time does not know what object to. Only when running.

The getInstance method creates an object based on the passed in full class name. If the full class name passed in is different, the object created will be different.

The random number + switch structure is used to control the difference of classPath, so as to dynamically create different objects.

1.6 get the complete structure of the class

For clarity, some preparatory work is needed.

The first is annotation: previously, annotation was mainly used in reflection. Now let's use it. Here is a custom annotation

@Repeatable(value = MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
public @interface MyAnnotation {
​
    // Defining an attribute is a bit like a method, but it is actually an attribute. The return value is an attribute type. If you need to receive multiple attribute values, you can define it as an array
    String[] value() default "Ha ha ha";
}

A custom interface:

public interface MyInterface {
​
    void info();
}
​

A parent class with generics

public class Creature<T> implements Serializable {
    private static final long serialVersionUID = 1L;
​
    private char gender;
    public double weight;
​
    private void breath() {
        System.out.println("Biological respiration");
    }
    public void eat() {
        System.out.println("Creatures eat");
    }
}

A specific class:

@MyAnnotation(value = "hi")
public class FullPerson extends Creature<String> implements Comparable<String>, MyInterface {
​
    private String name;
    int age;
    public int id;
​
    public FullPerson() {}
    
    private static int staticMethodShow(int num){
            System.out.println("Private static method" + num);
            return num;
        }
    
    @MyAnnotation(value = "Private parametric structure")
    private FullPerson(String name){
        this.name = name;
    }
    FullPerson(String name, int age){
        this.name = name;
        this.age = age;
    }
    @MyAnnotation()
    private String show(String str) {
        System.out.println("My nationality is"+str);
        return str;
    }
    public String display(String interest) throws NullPointerException, ClassCastException{
        System.out.println("My interest is"+interest);
        return interest;
    }
​
    @Override
    public int compareTo(String o) {
        return 0;
    }
​
    @Override
    public void info() {
        System.out.println("I am a person");
    }
    @Override
    public String toString() {
        return "FullPerson{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}

The FullPerson class is annotated and has generics. It inherits the creation class and implements two interfaces. mub

The goal is to get the structure of the FullPerson class at runtime.

  • getFields method of Class

Class<FullPerson> clazz = FullPerson.class;
// get attribute
Field[] fields = clazz.getFields();
for (Field field : fields) {
    System.out.println(field);
}
// output
// public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.id
// public double com.xgzit.JavaBaseStudy.reflectStudy.Creature.weight

First, there must be a Class object with a runtime Class. All public properties can be obtained by using the getFields method. This involves the issue of permissions. You can't get properties without permission directly. All public attributes here include not only their own, but also the public attributes of all their parent classes.

1.6.1 properties

  • Getdeclaraedfields method of Class

Class<FullPerson> clazz = FullPerson.class;
// get attribute
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field);
}
// output
// private java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.name
// int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.age
// public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.id

The above code calls the getdeclaraedfields method. This method obtains all the attributes of the current class definition, excluding its parent class, and the obtained attributes ignore permissions.

  • getModifiers method gets the Modifier of the property, and the return value is of type int. That is, a number that represents a permission name. The corresponding relationship between this number and permission is defined in the Modifier class. The Modifier's static method toString can be used to translate the Modifier of permission back.

Class<FullPerson> clazz = FullPerson.class;
        // get attribute
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 1. Get permission modifier
            int modifier = field.getModifiers();
            System.out.println(modifier);
            // Modifier's toString method can translate int numbers into strings
            System.out.println(Modifier.toString(modifier));
        }
// output
2
private
0
​
1
public

You can see that 2 is private; 0 is the default. The default is not to write. 1 is public.

  • Public class of Field class <? > The GetType () method gets the type of the property

Class<FullPerson> clazz = FullPerson.class;
// get attribute
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    // Get type
    Class<?> type = field.getType();
    System.out.println(type);
​
}
// output
class java.lang.String
int
int

You can see the type of property obtained.

  • String getName() method of Field class: get variable name

Class<FullPerson> clazz = FullPerson.class;
// get attribute
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    // Get variable name
    String name = field.getName();
    System.out.println(name);
}
// output
name
age
id

1.6.2 method

  • getMethods method of Class: get all public methods, including those of the parent Class

Class<FullPerson> clazz = FullPerson.class;
// Acquisition method
Method[] methods = clazz.getMethods();
for (Method method : methods) {
    System.out.println(method);
}
// output
public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.compareTo(java.lang.String)
public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.compareTo(java.lang.Object)
public void com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.info()
public java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.display(java.lang.String)
public void com.xgzit.JavaBaseStudy.reflectStudy.Creature.eat()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
  • Getdeclaraedmethods method of Class: all methods of the current Class, ignoring permissions, excluding those of the parent Class

Class<FullPerson> clazz = FullPerson.class;
// Acquisition method
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    System.out.println(method);
}
// output
public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.compareTo(java.lang.String)
public int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.compareTo(java.lang.Object)
public void com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.info()
private java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.show(java.lang.String)
public java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.display(java.lang.String)
  • The Annotation[] getAnnotations() Method of the Method class obtains all annotations of the Method

Note that this can only be used to obtain the annotation whose lifecycle @ Retention is RetentionPolicy.RUNTIME. Moreover, the annotation should be able to act on the method.

Class<FullPerson> clazz = FullPerson.class;
// Acquisition method
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    Annotation[] annos = method.getAnnotations();
    for (Annotation anno : annos) {
        System.out.println(anno);
    }
}
// output
@com.xgzit.JavaBaseStudy.AnnotationStudy.MyAnnotation(value=[Ha ha ha])
  • int getModifiers() of the Method class gets the modifier of the Method. It is almost consistent with the modifier acquisition of attributes

Class<FullPerson> clazz = FullPerson.class;
// Acquisition method
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    int mod = method.getModifiers();
    System.out.println(mod);
    System.out.println(Modifier.toString(mod));
}
// output
1
public
4161
public volatile
1
public
1
public
2
private

1 stands for public, 4161 for public volatile and 2 for private

  • Method class <? > Getreturntype() gets the return value type of the method

Class<FullPerson> clazz = FullPerson.class;
        // Acquisition method
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            Class<?> returnType = method.getReturnType();
            System.out.println(returnType);
        }
// output
int
int
void
class java.lang.String
class java.lang.String
  • Get the Method name by String getName() of the Method class

Class<FullPerson> clazz = FullPerson.class;
// Acquisition method
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    String name = method.getName();
    System.out.println(name);
}
// output
compareTo
compareTo
info
show
display
  • Method class <? > [] getparametertypes get parameter types

Class<FullPerson> clazz = FullPerson.class;
// Acquisition method
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    String name = method.getName();
    System.out.print(name + "(");
    Class<?>[] parameterTypes = method.getParameterTypes();
    if (parameterTypes.length != 0){
        for (Class<?> parameterType : parameterTypes) {
            System.out.print(parameterType);
        }
    }
    System.out.println(")");
}
//
compareTo(class java.lang.String)
compareTo(class java.lang.Object)
info()
display(class java.lang.String)
show(class java.lang.String)
  • Method class <? > [] getexceptiontypes() method gets the exception thrown by the method

 Class<FullPerson> clazz = FullPerson.class;
// Acquisition method
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
    String name = method.getName();
    System.out.print(name +"\t");
    Class<?>[] exceptionTypes = method.getExceptionTypes();
    if (exceptionTypes.length != 0){
        System.out.print("throws");
        for (Class<?> exceptionType : exceptionTypes) {
            System.out.print(exceptionType);
        }
    }
    System.out.println();
}
//output
compareTo   
compareTo   
info    
display throwsclass java.lang.NullPointerExceptionclass java.lang.ClassCastException
show

1.6.3 constructor

  • Constructor of Class <? > [] getconstructors () method to obtain the constructor of the runtime Class.

    Note: the constructor here cannot obtain the parent class, but can only obtain its own public permission constructor

Class<FullPerson> clazz = FullPerson.class;
// Get constructor
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println(constructor);
}
// output
public com.xgzit.JavaBaseStudy.reflectStudy.FullPerson()
  • Constructor of Class <? > [] getdeclaraedconstructors() method obtains all constructors of the runtime Class itself, ignoring permissions

Class<FullPerson> clazz = FullPerson.class;
// Get constructor
Constructor[] constructors = clazz.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println(constructor);
}
// output
com.xgzit.JavaBaseStudy.reflectStudy.FullPerson(java.lang.String,int)
private com.xgzit.JavaBaseStudy.reflectStudy.FullPerson(java.lang.String)
public com.xgzit.JavaBaseStudy.reflectStudy.FullPerson()

You can also get the constructor's parameter list, modifiers and annotations. The method is similar to the method.

1.6.4 parent class

  • Class <? Super T > getsuperclass() method to get the parent class

Class<FullPerson> clazz = FullPerson.class;
// Get parent class
Class<? super FullPerson> superclass = clazz.getSuperclass();
System.out.println(superclass);
// class com.xgzit.JavaBaseStudy.reflectStudy.Creature

The parent Class obtained is also an object of Class.

  • The Type getGenericSuperclass() method of Class gets the parent Class with generics. The return value is of type type. Type is an interface

Class<FullPerson> clazz = FullPerson.class;
// Get parent class with generics
Type superclass = clazz.getGenericSuperclass();
System.out.println(superclass);
// output
com.xgzit.JavaBaseStudy.reflectStudy.Creature<java.lang.String>
  • Gets the generic type of the parent class

Class<FullPerson> clazz = FullPerson.class;
// Get parent class with generics
Type superclass = clazz.getGenericSuperclass();
System.out.println(superclass.getClass()); // class sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
// Strong rotation
ParameterizedType para = (ParameterizedType) superclass;
Type[] arguments = para.getActualTypeArguments();
for (Type argument : arguments) {
    System.out.println(argument.getTypeName()); // java.lang.String
}

Why can I turn strongly? Because superclass is of this type. You can use superclass.getClass() to get the type of the runtime class. You know why you can turn strongly.

1.6.5 interface, package and annotation

  • Class<?> [] getinterfaces() gets the interface implemented by the runtime class. This does not get the interface implemented in the parent class.

Class<FullPerson> clazz = FullPerson.class;
// Get the implemented interface
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
    System.out.println(anInterface);
}
// output
interface java.lang.Comparable
interface com.xgzit.JavaBaseStudy.interfacepack.MyInterface
  • Package getPackage() gets the package where the runtime class is located

Class<FullPerson> clazz = FullPerson.class;
// Get the package
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
// output
package com.xgzit.JavaBaseStudy.reflectStudy
  • Annotation[] getAnnotations() gets the annotation above the runtime class

Class<FullPerson> clazz = FullPerson.class;
// Get the annotation above the class
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
    System.out.println(annotation);
}
// output
@com.xgzit.JavaBaseStudy.AnnotationStudy.MyAnnotation(value=[hi])

This section obtains the complete structure of the Class, which is actually the use of the methods in the Class. It also involves Field Class, Method Class, Constructor Class, Package Class and Annotation Class. That is, each structure in a Class has a Class corresponding to it. Calling the methods of these classes can do the corresponding work. In essence, it is the knowledge of classes and objects.

1.7 call the specified properties and methods of the runtime class

1.7.1 specifying attributes

  • Field getField(String name) method of Class: get the specified attribute

    This method can only get the properties of public permissions

Class<FullPerson> clazz = FullPerson.class;
// Create an object for the runtime class
FullPerson person = clazz.newInstance();
// Gets the specified property
Field id = clazz.getField("id");
// Set the value of the current property. Parameter 1 is the object and parameter 2 is the value
id.set(person, 1001);
// Gets the property value of the object
int o = (int) id.get(person);
System.out.println(o); // 1001
  • Field getdeclaraedfield (string name) method of Class: get the specified attribute

    This method can get all the properties of the runtime class and ignore the permissions

Class<FullPerson> clazz = FullPerson.class;
// Create an object for the runtime class
FullPerson person = clazz.newInstance();
// Gets the specified property
Field name = clazz.getDeclaredField("name");
// Set permissions to get private
name.setAccessible(true);
// Set the value of the current property. Parameter 1 is the object and parameter 2 is the value
name.set(person, "Tom");
// Gets the property value of the object
String o = (String) name.get(person);
System.out.println(o);

Note that setAccessible(true) must be set when accessing private properties. Otherwise, an error will be reported.

1.7.2 designation method

  • Method getmethod (string name, Class <? >... Parametertypes) method of Class: get the specified method

    getMethod can only obtain methods with public permissions

Class<FullPerson> clazz = FullPerson.class;
// Create an object for the runtime class
FullPerson person = clazz.newInstance();
// Gets the specified method. The first parameter is the method name, and the following parameter is the formal parameter type of the method.
Method display = clazz.getMethod("display", String.class);
System.out.println(display);
// output
public java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.display(java.lang.String) throws java.lang.NullPointerException,java.lang.ClassCastException
  • Method getdeclaraedmethod (string name, class <? >... Parametertypes) gets the specified method

    This method can obtain all permissions.

Class<FullPerson> clazz = FullPerson.class;
// Create an object for the runtime class
FullPerson person = clazz.newInstance();
// Gets the specified method
Method show = clazz.getDeclaredMethod("show", String.class);
System.out.println(show);
// Set permissions to get private
show.setAccessible(true);
show.invoke(person, "haha");
// output
private java.lang.String com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.show(java.lang.String)
My nationality is haha

Similarly, the setAccessible(true) method must be called to use private permission methods.

The calling method is the method object show calling the invoke method.

Object invoke(Object obj, Object... args)

The first parameter of the invoke method is the object that invokes the method, followed by the parameters of the original method. The return value is the return value of the original method.

The following is an example of a static method:

Class<FullPerson> clazz = FullPerson.class;
// Create an object for the runtime class
FullPerson person = clazz.newInstance();
// Gets the specified method
Method show = clazz.getDeclaredMethod("staticMethodShow", int.class);
System.out.println(show); 
// Set permissions to get private
show.setAccessible(true);
show.invoke(person, 5); // Private static method 5
int invoke = (int) show.invoke(clazz, 5);//Private static method 5
System.out.println(invoke);  // 5
​
// output
private static int com.xgzit.JavaBaseStudy.reflectStudy.FullPerson.staticMethodShow(int)
Private static method 5
 Private static method 5
5

show.invoke(person, 5); Is to call static methods through objects. show.invoke(clazz, 5) is called through a class.

The second method is recommended.

In fact, this call is OK. Write null directly. Because this is a static method, you don't need an object to call it. show is obtained through clazz, and it is known that it is called through class objects.

show.invoke(null, 5); //Private static method 5

1.7.3 specifying constructors

  • getConstructor method gets the specified common permission constructor.

Class<FullPerson> clazz = FullPerson.class;
Constructor<FullPerson> constructor = clazz.getConstructor();
System.out.println(constructor); // public com.xgzit.JavaBaseStudy.reflectStudy.FullPerson()
  • Getdeclaraedconstructor gets the specified constructor, ignoring permissions.

Class<FullPerson> clazz = FullPerson.class;
Constructor<FullPerson> constructor = clazz.getDeclaredConstructor(String.class);
System.out.println(constructor); // private com.xgzit.JavaBaseStudy.reflectStudy.FullPerson(java.lang.String)

If you want to use a private constructor, you also need to call the setAccessible(true) method

Class<FullPerson> clazz = FullPerson.class;
Constructor<FullPerson> constructor = clazz.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
FullPerson xxxx = constructor.newInstance("xxxx");
System.out.println(xxxx);
// output
FullPerson{name='xxxx', age=0, id=0}

We often use the Class object to directly call newInstance and call the parameterless constructor. This is more general.

2. Dynamic agent

The proxy model has been described earlier.

It is mainly static proxy. The characteristic is that the proxy class and the class of the target object are determined during compilation, which is not conducive to the expansion of the program. At the same time, each agent class can only serve one interface, so there must be too many agents in program development.

We prefer to use a proxy class to complete all the proxy functions.

That's dynamic proxy. When the program is running, dynamically create the proxy object of the target class as needed.

2.1 static agent

Review static agents.

An interface is required.

public interface ClothFactory {
​
    void produceCloth();
}

A static proxy class.

public class ProxyClothFactory implements ClothFactory{
​
    private ClothFactory factory;
​
    @Override
    public void produceCloth() {
        System.out.println("Acting factory to do front work");
        // The agent does its own core work
        factory.produceCloth();
        System.out.println("Act as an agent for the factory to finish the work");
    }
​
    public ProxyClothFactory(ClothFactory clothFactory){
        this.factory = clothFactory;
    }
}

A proxy class

public class QdClothFactory implements ClothFactory{
​
    @Override
    public void produceCloth() {
        System.out.println("Jordan factory produces a batch of sports shoes");
    }
}

use:

    @Test
    public void test1() {
        QdClothFactory qd = new QdClothFactory();
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(qd);
        proxyClothFactory.produceCloth();
    }

Both the proxy class and the proxy class must implement the same interface.

2.2 dynamic agent

One interface

public interface Human {
    /** Gain faith**/
    String getBelief();
    
    /** Eat**/
    void eat(String food);
}

A proxy class

public class SuperMan implements Human{
​
    @Override
    public String getBelief() {
        return "Superman saves the earth";
    }
​
    @Override
    public void eat(String food) {
        System.out.println("Superman likes to eat"+food);
    }
}

A proxy class:

Think about how to dynamically create a proxy class according to the proxy class loaded into memory?

When a method is called through the object of the proxy class, how to dynamically call the method with the same name in the proxy class?

public class ProxyFactory {
​
    /** Call this method to return a proxy class object**/
    public static Object getProxyInstance(Object obj) {
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        // Use the static method newProxyInstance of Proxy to get the Proxy class object
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), handler);
    }
}

The above proxy is a general method to obtain proxy class objects. You need to receive an object of a proxied class. Then we call the static method of the Proxy class newProxyInstance to get the proxy object. This newproxyinstance is an API provided by JDK. Take a look.

   
 @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {   
        //Check that the InvocationHandler implementation class object cannot be null, otherwise an exception will be thrown
        Objects.requireNonNull(h);
        // Get all the interfaces
        final Class<?>[] intfs = interfaces.clone();
        // security check
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
​
        // Use the provided class loader and interface to get the same class object
        Class<?> cl = getProxyClass0(loader, intfs);
​
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            // Get constructor
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // The maximum number of interfaces cannot exceed 65535
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        //If the proxy class defined by the given loader
        //If the given interface exists, this will only return a cached copy;
        //Otherwise, it creates a proxy class through ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

This method newProxyInstance finally creates a proxy class object for us dynamically according to the class loader of the proxy class, the interface implemented by the proxy class, and an InvocationHandler implementation class object provided by us.

Take a look at the InvocationHandler interface

public interface InvocationHandler {
​
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

This interface is relatively simple, with only one invoke Method. Receive 3 parameters. One is the Object, the second is the Method class Object, and the third is the parameter array of Object array type.

When we call a method using the obtained proxy class object, we will call the invoke method of the interface implementation class.

We need to provide such an implementation class object.

public class MyInvocationHandler implements InvocationHandler {
​
    /** Save proxied class object**/
    private Object obj;
​
    /** Constructor to receive the proxied object**/
    public MyInvocationHandler(Object obj){
        this.obj = obj;
    }
    // When we call method a through the object of the proxy class, we will automatically call the invoke method here
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
​
        System.out.println("The agent class does the front work");
        // method.invoke is the core work done by the proxy class itself
        Object returnVal = method.invoke(obj, args);
        System.out.println("The agent class does the closing work");
        return returnVal;
    }
}

This implementation class needs to receive the object of the proxy class. Because this object needs to call its own method.

In the invoke method, like the static proxy, you can do some pre and post work. But in the middle, the proxy object must work by itself.

Object returnVal = method.invoke(obj, args);

Test:

// Agent superMan
SuperMan superMan = new SuperMan();
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
proxyInstance.eat("beauty");
// Proxy qdClothFactory
QdClothFactory qdClothFactory = new QdClothFactory();
ClothFactory instance = (ClothFactory) ProxyFactory.getProxyInstance(qdClothFactory);
instance.produceCloth();

It can be seen that the general method getProxyInstance for obtaining proxy objects can proxy not only superMan but also qdcloshfactory

Using dynamic proxies eliminates the need to create multiple proxy classes.

In real time, we found that in the ProxyFactory class, the InvocationHandler implementation class object we created only once. Can I use the anonymous implementation class of the interface? Yes. Code transformation

public class ProxyFactory {
​
    /** Call this method to return a proxy class object**/
    public static Object getProxyInstance(Object obj) {
        // Use the static method newProxyInstance of Proxy to get the Proxy class object
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        return method.invoke(obj, args);
                    }
                });
    }
}

Is that it? Next, we will learn about the new features of JDK8. Use lambda expressions. It can be modified again.

public class ProxyFactory {
​
    /** Call this method to return a proxy class object**/
    public static Object getProxyInstance(Object obj) {
        // Use the static method newProxyInstance of Proxy to get the Proxy class object
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), (proxy, method, args) -> method.invoke(obj, args));
    }
}

See the next section for details of lambda expressions.

2.3 brief introduction of AOP and dynamic agent

It is difficult to see the advantages of this dynamic Proxy in the Proxy and InvocationHandler described earlier. Let's take a look at the dynamic Proxy in Spring.

situation:

Assuming that the same code segment appears in three places, we generally think of extracting the same code into A method A, and then calling this method in three places.

This is the most common way we deal with the same code. However, these three code segments are coupled with the extracted method A.

The most ideal effect is that method A can be executed in these three code segments, and there is no need to call it directly by hard coding in the program.

 

example:

Write a general method class

public class GeneralMethod {
​
    public void method1(){
        System.out.println("General method 1");
    }
​
    public void method2(){
        System.out.println("General method 2");
    }
}
​

Transform the ProxyFactory class.

public class ProxyFactory {
​
    /** Call this method to return a proxy class object**/
    public static Object getProxyInstance(Object obj) {
        // Use the static method newProxyInstance of Proxy to get the Proxy class object
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(), (proxy, method, args) -> {
                    GeneralMethod humanUtil = new GeneralMethod();
                    humanUtil.method1();
                    Object invoke = method.invoke(obj, args);
                    humanUtil.method2();
                    return invoke;
                });
    }
}

In fact, there is no difference, that is, some general things are done in front of and behind the proxy class.

Topics: Java reflection Dynamic Proxy