Java reflection mechanism

Overview of Java reflection mechanism

Reflection concept

  • 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, a Class object is generated in the method area of heap memory (a Class has only one Class object), which contains the complete Class structure information. 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 vividly call it reflection.

Dynamic language and static language

  • Dynamic language is a kind of language that can change its structure at runtime: for example, new functions, objects and even code can be introduced, existing functions can be deleted or other structural changes. Generally speaking, the code can change its structure according to some conditions at run time.
    Main dynamic languages: Object-C, c#, JavaScript, PHP, Python, Erlang.
  • Static language corresponds to dynamic language, and the language with immutable runtime structure is static language. Such as Java, C, C + +.

Java is not a dynamic language, but Java can be called a "quasi dynamic language". That is, Java has certain dynamics. We can use reflector and bytecode operation to obtain characteristics similar to dynamic language. The dynamic nature of Java makes programming more flexible!

Functions provided by Java 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 run time
  • Call the member variables and methods of any object at run time
  • Processing annotations at run time
  • Generate dynamic proxy

Main API s related to reflection

  • java.lang.Class: represents a class
  • java.lang.reflect.Method: method representing a class
  • java.lang.reflect.Field: represents the member variable of the class
  • java.lang.reflect.Constructor: constructor representing a class
  • java.lang.reflect.Constructor: constructor representing a class

Understand Class and get an instance of Class

Class class

  • The following methods are defined in the Object Class, which will be inherited by all subclasses: the type of the return value of the method above public final Class getClass() 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 looks in the mirror: 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 has only one Class instance in the JVM
    • A class object corresponds to a. Class file loaded into the JVM
    • Each Class instance will remember which Class instance it was generated from
    • All loaded structures in a Class can be completely obtained through Class
    • 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
  • Common methods of Class
Method nameFunction description
static Class forName(String name)Returns the Class object with the specified Class name
Object newInstance()Call the default constructor to return an instance of the Class object
getName()Returns the name of the entity (Class, interface, array Class, basic type or void) represented by this Class object
Class getSuperClass()Returns the Class object of the parent Class of the current Class object
Class [] getInterfaces()Gets the interface of the current Class object
ClassLoader getClassLoader()Returns the class loader for this class
Class getSuperclass()Returns the Class representing the superclass of the entity represented by this Class
Constructor[] getConstructors()Returns an array containing some Constructor objects
Field[] getDeclaredFields()Returns an array of Field objects
Method getMethod(String name,Class ... paramTypes)Returns a Method object whose formal parameter type is paramType
//Application examples of reflection
//test4.Person is the Person class under the test4 package
String str = "test4.Person";
Class clazz = Class.forName(str);
Object obj = clazz.newInstance();
Field field = clazz.getField("name");
field.set(obj, "Peter");
Object name = field.get(obj);
System.out.println(name);
  • Get an instance of Class (four methods)

    • Premise: if a specific class is known, it is obtained through the class attribute of the class. This method is the most safe and reliable, and the program performance is the highest
      Example: Class clazz = String.class;
    • Premise: if the instance of a Class is known, call the getClass() method of the instance to obtain the Class object
      Example: Class clazz = obj.getClass();
    • Premise: 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 may be thrown
      Example: Class clazz = Class.forName("java.lang.String");
    • Other methods (not required)
      ClassLoader cl = this.getClass().getClassLoader();
      Class clazz4 = cl.loadClass("full class name of class");
  • What types can have Class objects?

    • Class: external class, member (member internal class, static internal class), local internal class, anonymous internal class
    • interface: interface
    • []: array
    • enum: Enumeration
    • Annotation: annotation @ interface
    • primitive type: basic data type
    • void
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;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// As long as the element type is the same as the dimension, it is the same Class
System.out.println(c10 == c11);

Class loading and ClassLoader understanding

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.lang.Class object representing this class as the access entry (i.e. reference address) of the 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 conforms to the JVM specification.
    • 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 used to construct class information, not the constructor to construct the class object.).
    • When initializing a class, if it is found that its parent class has not been initialized, the initialization of its parent class needs to be triggered first.
    • Virtual opportunity ensures that the < clinit > () methods of a class are locked and synchronized correctly in a multithreaded environment.
public class ClassLoadingTest {
	public static void main(String[] args) {
		System.out.println(A.m);
	} 
}
class A {
	static { 
		m = 300;
	}
	static int m = 100;
}
//Step 2: m=0 after link
//Step 3: after initialization, the value of m is executed by the < clinit > () method. The < clinit > () method of class constructor of A 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;
		// }

When does class initialization occur?

  • Active reference of class (class initialization must occur)
    • When the virtual machine starts, initialize the class where the main method is located first
    • new is an object of a class
    • Call static members (except final constants) and static methods of the class
    • Use the methods of the java.lang.reflect package to make reflection calls to the class
    • When initializing a class, if its parent class is not initialized, its parent class will be initialized first
  • Passive reference of class (class initialization will not occur)
    • When accessing a static domain, only the class that actually declares the domain will be initialized
    • When a static variable of a parent class is referenced through a subclass, subclass initialization is not caused
    • Defining a class reference through an array does not trigger the initialization of this class
    • Reference constants do not trigger the initialization of this class (constants are stored in the constant pool of the calling class in the link phase)
public class ClassLoadingTest {
	public static void main(String[] args) {
	// Active reference: it will certainly lead to the initialization of A and Father
	// A a = new A();
	// System.out.println(A.m);
	// Class.forName("com.atguigu.java2.A");
	// Passive reference
	A[] array = new A[5];//It will not cause initialization of A and Father
	// System.out.println(A.b);// Only Father will be initialized
	// System.out.println(A.M);// It will not cause initialization of a and Father
	}
	static {
		System.out.println("main Class in which");
	} 
}

class Father {
	static int b = 2;
	static {
		System.out.println("The parent class is loaded");
	}
}
class A extends Father {
	static {
		System.out.println("Subclass loaded");
		m = 300;
	}
	static int m = 100;
	static final int M = 1;
}

Role of class loader

  • The function of class loading: 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.lang.Class object representing this class in the heap as the access entry to the 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.
  • Class loader is used to load classes into memory. The JVM specification defines loaders for classes of the following types.
//1. Get a system class loader
ClassLoader classloader = ClassLoader.getSystemClassLoader();
System.out.println(classloader);
//2. Get the parent class loader of the system class loader, that is, the extension class loader
classloader = classloader.getParent();
System.out.println(classloader);
//3. Get the parent class loader of the extension class loader, that is, the boot class loader
classloader = classloader.getParent();
System.out.println(classloader);
//4. Test which class loader loads the current class
classloader = Class.forName("exer2.ClassloaderDemo").getClassLoader();
System.out.println(classloader);
//5. Test which class loader loads the Object class provided by JDK
classloader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classloader);
//*6. A main method of class loader: getResourceAsStream(String str): get the input stream of the specified file under the class path
InputStream in = null;
in = this.getClass().getClassLoader().getResourceAsStream("exer2\\test.properties");
System.out.println(in);

Create an object for the runtime class

  • What can I do with a Class object?
    Create Class object: call newInstance() method of Class object
    Requirements: 1) class must have a constructor without parameters. 2) The constructor of the class needs sufficient access rights.
  • Can't you create an object without a parameterless constructor?
    no As long as the constructor in the class is explicitly called during the operation and the parameters are passed in, the operation can be instantiated.
    The steps are as follows:
    • 1. Get the constructor of the specified parameter type of this Class through getdeclaraedconstructor (Class... parameterTypes) of Class class.
    • 2. Pass an object array into the formal parameters of the constructor, which contains all the parameters required by the constructor.
    • 3. Instantiate the object through the Constructor.

These are the places where reflection mechanism is most used.

//1. Obtain the corresponding Class object according to the full Class name
String name = "atguigu.java.Person";
Class clazz = Class.forName(name);
//2. Call the Constructor of the specified parameter structure to generate an instance of Constructor
Constructor con = clazz.getConstructor(String.class,Integer.class);
//3. Create the object of the corresponding class through the instance of Constructor and initialize the class properties
Person p2 = (Person) con.newInstance("Peter",20);
System.out.println(p2);

Gets the complete structure of the runtime class

Using reflection, you can get:

  • 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 > [] getdeclaraedconstructors() returns all construction methods declared by the Class represented by this Class object.
    • 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 [] getdeclaraedmethods() returns all the 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() gets all the 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 [] getdeclaraedfields() returns all fields of the Class or interface represented by this Class object.
    • In the Field method:
      • public int getModifiers() returns the modifier of this Field as an integer
      • public Class<?> Gettype() gets the property type of Field
      • public String getName() returns the name of the Field.
  • 6. Annotation related

    • get Annotation(Class 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. The package in which the class is located

    • Package getPackage()

Summary:

  1. In the actual operation, the operation code to obtain the class information is not often developed
  2. Be familiar with the function and reflection mechanism of java.lang.reflect package.
  3. How to get the names and modifiers of properties, methods and constructors.

Calls the specified structure of the runtime class

  • 1. Call the specified method
    Through reflection, the Method in the class is called and completed through the Method class. Steps:

    • Get a Method object through the getMethod(String name,Class... parameterTypes) Method of Class class, and set the parameter type required for this Method operation.
    • Then use Object invoke(Object obj, Object[] args) to call and pass the parameter information of the obj object to be set to the method.
    • Object invoke(Object obj, Object... args) Description:
      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
      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.
  • 2. Call the specified attribute
    In the reflection mechanism, you can directly operate the properties in the class through the Field class. You can set and obtain the property content 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
  • 3. Use of setAccessible method

    • Method, Field and Constructor objects all have setAccessible() methods.
    • setAccessible switches that enable and disable access security checks.
    • If the parameter value is true, it indicates that the Java language access check should be cancelled 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.

Application of reflection: dynamic proxy

  • Principle of agent design pattern:
    Wrap the object with a proxy object and replace the original object with the proxy object. Any call to the original object is made through a proxy. The proxy object determines whether and when method calls are transferred to the original object.
    The operation of the previous proxy mechanism belongs to static proxy, which is characterized in 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. It is best to complete all proxy functions through a proxy class.
  • 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.
    • Usage of dynamic agent: debugging and remote method call
  • Advantages of dynamic proxy over static proxy: 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.
  • Java Dynamic Proxy related API s
    • Proxy: it is a special operation class for 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 <? >... interfaces) creates a class object corresponding to a dynamic proxy class
      Static object newproxyinstance (classloader, loader, class <? > [] interfaces, invocationhandler h) directly creates a dynamic proxy object

Dynamic proxy steps

  • 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 a Subject interface Proxy through the Proxy's static method newproxyinstance (classloader, loader, class [] interfaces, invocationhandler h).
  • 4. Call the method of RealSubject implementation class through Subject proxy
//Step 1: create a class that implements the interface InvocationHandler
public Object invoke(Object theProxy(Object of proxy class), Method method(Object to call), Object[] params(Parameters required for method invocation)) throws Throwable{
	try{
		Object retval = method.invoke(targetObj, params);
		// Print out the result
		System.out.println(retval);
		return retval;
	}catch (Exception exc){
	}
}

//Step 2: create the proxy class and interface
RealSubject  implements Subject
	say(String name,int age)

//Step 3: static method through Proxy
RealSubject target = new RealSubject();
// Create a proxy to wrap the original implementation
DebugProxy proxy = new DebugProxy(target);
// Get a reference to the proxy through the Subject interface
Subject sub = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),new Class[] { Subject.class }, proxy);

//Step 4: call the method of RealSubject implementation class through Subject proxy
String info = sub.say("Peter", 24);
System.out.println(info);

Dynamic agent and AOP (aspect oriented programming)


The above are Proxy and InvocationHandler. It is difficult to see the advantages of this dynamic Proxy. The following is a more practical dynamic Proxy mechanism

Improved description: code segment 1, code segment 2, code segment 3 and dark code segment are separated, but code segments 1, 2 and 3 are coupled with A specific method A! The most ideal effect is that code blocks 1, 2 and 3 can execute method A without directly calling the method of dark code in the program by hard coding

public interface Dog{
	void info();
	void run();
}

public class HuntingDog implements Dog{
	public void info(){
		System.out.println("I'm a hound");
	}
	public void run(){
		System.out.println("I run fast");
	}
}

public class DogUtil{
	public void method1(){
		System.out.println("=====General simulation method I=====");
	}
	public void method2(){
		System.out.println("=====General simulation method II=====");
	}
}

public class MyInvocationHandler implements InvocationHandler{
	// Object to be proxied
	private Object target;
	public void setTarget(Object target){
		this.target = target;
	}
	// When all methods of the dynamic proxy object are executed, they will be replaced by the following invoke method
	public Object invoke(Object proxy, Method method, Object[] args) throws Exception{
		DogUtil du = new DogUtil();
		// Execute method1 in the DogUtil object.
		du.method1();
		// Execute the method method with target as the main call
		Object result = method.invoke(target , args);
		// Execute method2 in the DogUtil object.
		du.method2();
		return result;
	}
}

public class MyProxyFactory{
	// Generates a dynamic proxy object for the specified target
	public static Object getProxy(Object target) throws Exception{
		// Create a myinvocationhandler object
		MyInvokationHandler handler = new MyInvokationHandler();
		// 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 Test{
	public static void main(String[] args) 	throws Exception{
		// Create an original HuntingDog object as the target
		Dog target = new HuntingDog();
		// Creates a dynamic proxy with the specified target
		Dog dog = (Dog)MyProxyFactory.getProxy(target);
		dog.info();
		dog.run();
	}
}

When using Proxy to generate a dynamic Proxy, it often does not generate a dynamic Proxy out of thin air, which is meaningless. Usually, it generates a dynamic Proxy for the specified target object
This dynamic agent is called AOP agent in AOP. AOP agent can replace the target object. AOP agent contains all the methods of the target object. However, there are differences between the methods in AOP agent and the methods of the target object: the methods in AOP agent can insert some general processing before and after the execution of the target method

Tags: Java Back-end

Posted on Mon, 29 Nov 2021 15:35:04 -0500 by Zangakat