Principles of reflection and reflection in Spring, detailed case explanation

 

stay Making wheels: to realize a simple Spring IoC container Spring uses reflection when creating Bean instances and dependency injection, which is mentioned in the article. In this article, we will analyze the reflection and the principle of reflection in spring.

1, Reflection in Spring

1.1. Reflection when creating a Bean instance

//Through the class loader, the class object is obtained according to the class path
Class<?> clz = Thread.currentThread().getContextClassLoader().loadClass("org.deppwang.litespring.v1.service.PetStoreService");
//Generating Bean instances from class objects
return clz.newInstance();
Also note here: the theory of light is not enough. Remember: Java architecture project experience is always the core. If you don't have the latest Java architecture practice course and the big factory 30k + interview book, you can go to the small-scale Java architecture learning. Skirt: seven umbrella bar, zero clothes and zero umbrella (Digital homophony) you can find it under conversion. There are many new Java architecture project tutorials in it. You can also communicate with the old driver for advice!  

Reflection is reflected in clz.newInstance(); and the core code can be divided into two parts:

1. Get all the Constructor information (Constructor object) of the current class PetStoreService by reflection

// java.lang.Class.java
// Call the native method, when publicOnly is false
res = getDeclaredConstructors0(publicOnly);
// native method, obtain the construction method information from the class file in the jvm, and then convert it to the Constructor object
private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);
Copy code

2. Using reflection to generate instances by default construction method

// sun.reflect.NativeConstructorAccessorImpl.java
// Call the native method, var1 represents the parameter of the construction method, which is null at this time
return newInstance0(this.c, var1);
// native method, the real method to generate instances, and the construction method to execute class files < init >
private static native Object newInstance0(Constructor<?> var0, Object[] var1);
Copy code

1.2. Reflection when construction method depends on injection

// Get all the Constructor information of the current class through reflection (Constructor object)
Constructor<?>[] candidates = beanClass.getDeclaredConstructors();
// Set constructor parameter instance
Object[] argsToUse = new Object[parameterTypes.length];
argsToUse[i] = getBean(beanNames.get(i));
// Instantiate a Bean using a Constructor object with parameters. At this time, the reflection is used as above (newInstance0), but there are more parameters
return constructorToUse.newInstance(argsToUse);
Copy code

1.3. Reflection when setter() method relies on injection

// Get all Method information (Method object) of the current class through reflection
Method[] methods = bean.getClass().getDeclaredMethods();
// Get method parameter instance
Object propertyBean = getBean(propertyName);
// Call the setter() method through reflection execution. invoke: call the method, propertyBean as the parameter of the method
method.invoke(bean, propertyBean);
Copy code

Core code in bean.getClass().getDeclaredMethods();:

// java.lang.Class.java
// Call native method, publicOnly is false
getDeclaredMethods0(publicOnly);
// native Method, obtain Method information from the class file in the jvm, and then convert it to Method
private native Method[]      getDeclaredMethods0(boolean publicOnly);
Copy code

method.invoke(bean, propertyBean); core code in:

// sun.reflect.NativeMethodAccessorImpl.java
// Call native method, var1: bean, var2: propertyBean
return invoke0(this.method, var1, var2);
// native method to run bytecode instructions in class file
private static native Object invoke0(Method var0, Object var1, Object[] var2);
Copy code

1.4 reflection of @ Autowired dependency injection

// Get all Field information (Field object) of the current class through reflection
Field[] fields = bean.getClass().getDeclaredFields();
// Judge whether the field has @Autowired annotation
Annotation ann = field.getAnnotation(Autowired.class);
// Setting fields to be connectable is equivalent to changing non public (private, default, protect) to public
field.setAccessible(true);
// Setting the value of a field by reflection
field.set(bean, getBean(field.getName()));
Copy code

Core code in bean.getClass().getDeclaredFields();:

// java.lang.Class.java
// Call the native method, when publicOnly is false
getDeclaredFields0(publicOnly);
// The native method obtains the Field information of the class file from the jvm and converts it into a Field
private native Field[]       getDeclaredFields0(boolean publicOnly);
Copy code

Core code in field.set(bean, getBean(field.getName())):

// sun.reflect.UnsafeObjectFieldAccessorImpl.java
// Call the native method to set (modify) the field value at the specified offset fieldOffset of the target object var1 to var2. var1 is bean, var2 is parameter instance
unsafe.putObject(var1, this.fieldOffset, var2);

// sun.misc.Unsafe.java
// native method, directly modify the data of the object field in the heap
public native void putObject(Object var1, long var2, Object var4);
Copy code

2, Class files and class objects

class files are compiled from java files, including field tables, method tables, and < init > methods (construction methods).

When the Class loader loads the Class file into the virtual machine metadata area (method area, jdk1.7), the virtual machine creates a corresponding Class object (Class instance). The Class file is transformed from a static structure stored on disk to a runtime structure stored in memory.

We can think that a class (class file) corresponds to a class object, and all objects of the current class share a class object. The class object serves as an entry to the class file stored in the jvm.

package java.lang;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;

public final class Class<T> {
    private native Field[]       getDeclaredFields0(boolean publicOnly);
    private native Method[]      getDeclaredMethods0(boolean publicOnly);
    private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);

    // ReflectionData caching reflection objects
    private static class ReflectionData<T> {
        volatile Field[] declaredFields;
        volatile Field[] publicFields;
        volatile Method[] declaredMethods;
        volatile Method[] publicMethods;
        volatile Constructor<T>[] declaredConstructors;
        volatile Constructor<T>[] publicConstructors;
        ...
    }
}
Copy code

2.1. How to obtain class objects

// 1. Through objects
Class cls = object.getClass();
// Object.java
public final native Class<?> getClass();

// 2. Through class loader
Class cls = Thread.currentThread().getContextClassLoader().loadClass("org.deppwang.litespring.v1.service.PetStoreService");

// 3. Through the Class class, it is essentially through the Class loader
Class cls = Class.forName("org.deppwang.litespring.v1.service.PetStoreService");
// Class.java
private static native Class<?> forName0(String name, boolean initialize,
                                            ClassLoader loader,
                                            Class<?> caller)
Copy code

3, Reflection method

The following are common reflection methods.

3.1. Feild related

Field[] fields = cls.getFields(); // Get all public fields (including the parent class)
Field[] fields = cls.getDeclaredFields(); // Gets all fields of the current class (excluding the parent class), including public and non-public
Field field = cls.getDeclaredField("fieldName"); // Specifies to get a Field of the current class
field.set(Object, Object); // Set (modify) field value
field.get(Object); // Get field value
Copy code

field.get(Object) core code:

// Call the native method to get the corresponding value of the field
return unsafe.getObject(var1, this.fieldOffset);

// The native method, which gets the object at the specified location from the heap
public native Object getObject(Object var1, long var2);
Copy code

3.2 Method related

Method[] methods = cls.getMethods(); // Get all public methods (including the parent class)
Method[] methods = cls.getDeclaredMethods(); // Get all methods of the current class (excluding the parent class), including public and non-public
method.invoke(Object instance, Object... parameters); // Operation method
Copy code

Usage scenario of run method: either modify the data of object, such as void setter() method, or get the return result of execution method.

String result = method.invoke().toString();
Copy code

3.3. Constructor related

Constructor<?>[] constructors = cls.getConstructors(); // Get all public constructors (including the parent class)
Constructor<?>[] constructors = cls.getDeclaredConstructors(); // Gets all constructors (excluding the parent) of the current class, both public and non-public
constructor.newInstance(Object... parameters); // Operation construction method
Copy code

When the constructor is not explicitly written, the Java compiler will build a default constructor for the class < init >

4, native method

Java 1.1 adds "Java Native Interface" (JNI). JNI is a very inclusive programming interface that allows us to call native methods from Java applications. Native methods are written in other languages (C, C + + or assembly language, etc.). The native method is used to implement functions that Java cannot handle.

4.1 simple example

A simple example of using the Java local interface (JNI) in Java.

  • Environment: jdk8, Mac OS 10.15
// Main.java
public class Main {
    public native int intMethod(int i);
    static {
        // Load libMain.dylib at startup
        System.loadLibrary("Main");
    }
    public static void main(String[] args) {
        System.out.println(new Main().intMethod(2));
    }
}
Copy code
// Main.c:
// Introduce Main.h
#include "Main.h"

// Equivalent to Java main in method inheriting "Main.h"
JNIEXPORT jint JNICALL Java_Main_intMethod(
    JNIEnv *env, jobject obj, jint i)
{
    return i * i;
}
Copy code

Compile and run:

// Generate Main.class and Main.h at the same time
javac Main.java -h .
// Generate libMain.dylib according to Main.c
gcc -dynamiclib -O3 \
    -I/usr/include \
    -I$JAVA_HOME/include \
    -I$JAVA_HOME/include/darwin \
    Main.c -o libMain.dylib
// Specify the path of the library as the current path
java -cp . -Djava.library.path=$(pwd) Main
Copy code

Output:

4
 Copy code
/* Main.h .h As header file*/
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Main */

#ifndef _Included_Main
#define _Included_Main
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Main
 * Method:    intMethod
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_Main_intMethod
  (JNIEnv *, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif
Copy code
javac Main.java -h .
// Split into two commands
javac Main.java
javah -jni Main
Copy code

4.2 principle

When running Main.class, load libMain.dylib into the virtual machine, the JVM calls the Java ﹣ main ﹣ intmethod of libMain.dylib, passes in the parameter, libMain.dylib runs directly by the system, and returns the result.

  • *env is used to convert java type data to local (in this case, C language) type data
  • jint is also a java data type. Java basic data types can be mapped (used) without * env conversion
/*C code*/
JNIEXPORT void JNICALL Java_ClassName_MethodName
  (JNIEnv *env, jobject obj, jstring javaString)
{
    /*Get the native string from javaString*/
    const char *nativeString = (*env)->GetStringUTFChars(env, javaString, 0);

    /*Do something with the nativeString*/

    /*DON'T FORGET THIS LINE!!!*/
    (*env)->ReleaseStringUTFChars(env, javaString, nativeString);
}
Copy code

4.3 reference

5, Summary

Reflection, where to reflect the literal meaning of reflection?

It can be understood that the reflection object can be obtained through the native method, and the operation of the reflection object, like a mirror, will be reflected on the original object.

We find that the relationship between reflection and native method:

  • Get the fields, methods, construction method objects, and implement the native() method
  • Get the field value, set and modify the field value, and implement the native() method
  • Run method, native() method implementation
  • Run construction method, native() method implementation

We can conclude that reflection is implemented by the native method.

We say that we can achieve a function through reflection, we can also say that

  • Realized by reflection method
  • Implemented through reflection API
  • Implemented by native method

 

 

Reflection is a kind of unconventional (native method implementation) way to obtain class file information, run class file bytecode instructions and operate object data.

Bottom line: reflection is a runtime ability to acquire and modify object data.

About Runtime: Java is a static language, compiled first and then run. No code is executed at compile time. All code is executed at run time.

  • Finally note: the theory of light is not enough. Remember: Java architecture project experience is always the core. If you don't have the latest Java architecture practice tutorial and big factory 30k + interview book, you can go to the small-scale Java architecture learning (Digital homophony) you can find it under conversion. There are many new Java architecture project tutorials in it. You can also communicate with the old driver for advice!  

    The text and pictures of this article come from the Internet and my own ideas. They are only for learning and communication. They have no commercial use. The copyright belongs to the original author. If you have any questions, please contact us in time for handling

Tags: Java jvm Spring Programming

Posted on Wed, 06 May 2020 03:45:01 -0700 by pernest