This is the 20th day of my participation in the Genwen Challenge

Concept of reflection

  • Reflection:Refelection, reflection is a Java feature that allowsrunJava programs get information about themselves and can manipulate the internal properties of a class or object
    • Reflection allows you to get information at run time about the program or how each type of member in the program is a member
    • Objects in a program are typically identified at compile time. Java reflection allows you to dynamically create objects and invoke properties whose types are unknown at compile time
    • That is, an object can be created directly through reflection, even if the object type is unknown at compile time
  • Java reflection provides the following capabilities:
    • Determine the class to which any object belongs at run time
    • Constructs an object of any class at run time
    • To determine which member variables and methods any class has at run time, private methods can be called by reflection
    • Call a method of any object at run time

Principle of reflection

  • The core of reflection: Does the JVM dynamically load classes or invoke methods and access properties at run time, without needing to know in advance (say, compile time) what the running object is
  • Class loading:
    • Java reflection is built around the Class Class
    • First, understand the class loading mechanism:
      • The JVM uses the ClassLoader to load bytecode files, called class files, into the method area memory
Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.mypackage.MyClass");
Copy the code

The ClassLoader Class loads a Class based on its fully qualified name and returns a Class object

  • ReflectionData:
    • To improve reflection performance, caching must be provided
    • A useCaches static variable is used inside the class to flag whether caching is used
    • This value can be checked by the external sun.reflect.noCaches configuration to disable caching
    • The class provides a ReflectionData inner class that holds a cache of reflected data and declares a ReflectionData field
    • This field does not point to an instantiated ReflectionData object because it is lazily loaded and cached on demand later
// Flag whether to use caching, you can set external sun.reflect.nocaches to disable caching
private static boolean useCaches = true;

static class ReflectionData<T> {
	volatile Field[] declaredFields;
	volatile Field[] publicFields;
	volatile Method[] declaredMethods;
	volatile Method[] publicMethods;
	volatile Constructor<T>[] declaredConstructors;
	volatile Constructors<T>[] publicConstructors;
	volatile Field[] declaredPublicFields;
	volatile Method[] declaredPublicMethods;
	final int redefinedCount;

	ReflectionData(int redefinedCount) {
		this.redefinedCount = redefinedCount; }}// This is SoftReference, which may be reclaimed when memory resources are tight
	// Volatile ensures correct reads and writes in multithreaded environments
	 private volatile transient SoftReference<RefelectionData<T>> reflectionData;

	// This is mainly used to compare with redefinedCount in ReflectionData
	// If the two values are not equal, the data cached by ReflectionData has expired
	private volatile transient classRedefinedCount = 0;
Copy the code

The main uses of reflection

  • The most important use of reflection is to develop common frameworks
    • Many frameworks are configured, with beans configured through XML files
    • To ensure the universality of the framework, it is necessary to load different objects or classes and call different methods according to the configuration file
    • To use reflection, the runtime dynamically loads the objects that need to be loaded
  • Example:
    • Struts. XML is used to configure the Action in Struts 2:
	  <action name="login"
               class="org.ScZyhSoft.test.action.SimpleLoginAction"
               method="execute">
           <result>/shop/shop-index.jsp</result>
           <result name="error">login.jsp</result>
       </action>
Copy the code
  • The configuration file is mapped to the Action
  • When requesting the View layer, the request will be StrutsPrepareAndExecuteFilter intercept
  • StrutsPrepareAndExecuteFilterIt will be created dynamicallyActionThe instance
    • Request the login. The action
    • StrutsPrepareAndExecuteFilter parses the struts. The XML file
    • Retrieve the action whose name is login from the action
    • Create the SimpleLoginAction instance based on the class attribute
    • Use the invoke method to call the execute method
  • Reflection is at the heart of various container implementations

Use of reflection

  • Reflection related classes in StrutsPrepareAndExecuteFilter package
  • Reflection can be used for:
    • Determines the class to which the object belongs
    • Get the class object
    • Construct any object
    • Calling an object
  • There are nine predefined Class objects:
    • Basic Java types: Boolean, byte, char, short, int, long, float, double
    • The keyword void is also represented as a class object via the class attribute
    • A static TYPE field for a wrapper class or void class:
      • Integer.TYPE == int.class
      • Integer.class == int.class
    • Class instance object of array type:
      • Class<String[]> clz = String[].class;
      • How to compare an array’s Class objects for equality:
        • The dimension of the array
        • Array type
        • Class isArray(), used to determine whether an array type is represented

Get the Class object

  • Use the forName static method of Class:
public staticClass<? > forName(String className);/* Use this method in JDBC to load the database driver */
Class.forName(driver);
Copy the code
  • Get the class of an object directly:
Class<? > klass=int.class; Class<? > classInt=Integer.TYPE;Copy the code
  • Call the getClass() method of the object:
StringBuilder str=new StringBuilder("A"); Class<? > klass=str.getClass();Copy the code

Determines if it is an instance of a class

  • In general, the instanceof keyword is used to determine whether it is an instanceof a class
  • In reflection, you can use the isInstance() method of a Class object to determine whether it is an instance of a Class, which is a native method
public native boolean isInstance(Object obj);
Copy the code

Create an instance

There are two main ways to generate instances of objects through reflection:

  • Use the Class object’s newInstance() method to create instances of the corresponding Class:
Class<? > c = String.class; Object str = c.newInstance();Copy the code
  • Obtaining the specified Constructor object from the Class object and then calling the Constructor object’s newInstance() method to create an instance: you can construct an instance of the Class using the specified Constructor
/* Get the Class object */ corresponding to StringClass<? > c=String.class;/* Gets the constructor */ of the String class with a String argument
Constructor constructor=c.getConstructor(String.class);

/* Create instance from constructor */
Object obj=constructor.newInstance("abc");
System.out.println(obj);
Copy the code

Access method

There are three main methods to get a Class object’s collection of methods:

  • getDeclaredMethods():Returns all methods declared by a class or interface:
    • Includes public, protected, default (package) access, and private methods
    • Methods of inheritance are not included
public Method[] getDeclaredMethods() throws SecurityException {}
Copy the code
  • getMethods():Object owned by a classpublicmethods
    • Include public methods of inherited classes
public Method[] getMethods() throws SecurityException {}
Copy the code
  • getMethod():Returns a specific method
    • The first parameter: method name
    • The following arguments: the method arguments correspond to the Class object
public Method getMethod(String name,Class
       ... parameterType) {}
Copy the code
  • Obtain method example:
public class MethodTest {
	public static void methodTest(a) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class<? > c = methodClass.class; Object object = c.newInstance(); Method[] methods = c.getMethods(); Method[] declaredMethods = c.getDeclaredMethods();// Get the add method in methodClass
		Method method = c.getMethod("add".int.class, int.class);
		// getMethods() gets all the methods
		System.out.println("GetMethods methods :");
		for (Method m:methods)
			System.out.println(m);
		// getDeclaredMethods()
		System.out.println("GetDeclaredMethods");
		for(Method m:declaredMethods) System.out.println(m); }}class methodClass {
	public final int n = 3;
	
	public int add(int a, int b) {
		return a + b;
	}
	
	public int sub(int a, int b) {
		returna * b; }}Copy the code

Program running results:

GetMethods:  public int org.ScZyhSoft.common.methodClass.add(int,int) public int org.ScZyhSoft.common.methodClass.sub(int,int) 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. The notifyAll () getDeclaredMethods access method:  public int org.ScZyhSoft.common.methodClass.add(int,int) public int org.ScZyhSoft.common.methodClass.sub(int,int)Copy the code

Methods obtained by getMethods() getMethods of the parent class

Gets constructor information

  • Get an instance of the Constructor Class through the getConstructor method of the Class
  • The newInstance method of the Constructor class creates an instance of an object:
public T newInstance(Objec ... initargs)
Copy the code

The newInstance method creates an instance of the object by calling the corresponding Constructor based on the parameters passed in

Gets information about a class’s member variables

  • GetFileds: Gets public member variables
  • GetDeclaredFields: Gets all declared member variables, but not the parent member variables

A method is called

  • Once you get a method from a class, you can invoke the method using invoke()
public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {}
Copy the code
  • Example:
public class InvokeTest {
	public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class<? > klass = method.class;// Create an instance of methodClass
		Object obj = klass.newInstance();
		// Get the add method for methodClass
		Method method = klass.getMethod("add".int.class, int.class);
		// add(1,4)
		Object result = method.invoke(obj, 1.4); System.out.println(result); }}class methodClass {
	public final int n = 3;
	public int add(int a, int b) {
		return a + b;
	}
	public int sub(int a,int b) {
		returna * b; }}Copy the code

Create arrays using reflection

  • Arrays are a special data type in Java that can be assigned to an Object Reference
  • An example of creating an array with reflection:
public static void ArrayTest(a) throws ClassNotFoundException { Class<? > cls = class.forName("java.lang.String");
	Object array = Array.newInstance(cls, 25);
	// Add data to the array
	Array.set(array, 0."C");
	Array.set(array, 1."Java");
	Array.set(array, 2."Python");
	Array.set(array, 3."Scala");
	Array.set(array, 4."Docker");
	// Get an item of data
	System.out.println(Array.get(array, 3));
}
Copy the code

The Array class is java.lang.reflect.Array, which creates Array objects through array.newinstance () :

public static Object newInstance(Class<? > componentType,int length) throws NegativeArraySizeException {
	return newArray(componentType, length);
}
Copy the code

The newArray method is a native method implemented in the HotSpot JVM.

private static native Object newArray(Class<? > componentType,int length) throws NegativeArraySizeException; ------------------------------------------------------------------------------------------------------------------------ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- newArray source directory: openjdk\hotspot\src\share\vm\runtime\reflection.cpp arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) {if (element_mirror == NULL) {
    THROW_0(vmSymbols::java_lang_NullPointerException());
  }
  if (length < 0) {
    THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
  }
  if (java_lang_Class::is_primitive(element_mirror)) {
    Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL);
    return TypeArrayKlass::cast(tak)->allocate(length, THREAD);
  } else {
    Klass* k = java_lang_Class::as_Klass(element_mirror);
    if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {
      THROW_0(vmSymbols::java_lang_IllegalArgumentException());
    }
    returnoopFactory::new_objArray(k, length, THREAD); }}Copy the code
  • ArrayOf the classsetandgetMethods arenativeMethod, specific implementation inHotSpot JVM, the corresponding relationship is as follows:
    • set: Reflection::array_set
    • get: Reflection::array_get

Invoke method

  • Invoke methods are called by many methods in Java, and many exceptions are thrown to invoke methods:
java.lang.NullPointerException
  at ......
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:497)
Copy the code

Invoke execution procedure

  • The invoke method is used to dynamically invoke an instance’s method at runtime, implementing the following:
@CallSensitive
public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
	if(! override) {if(! Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<? > caller = Refelection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor;if (ma == null) {
		ma = acquireMethodAccessor();
	}
	return ma.invoke(obj, args);
}
Copy the code

Permission check

  • AccessibleObjectA class isField,MethodandConstructorObject base class:
    • Provides the ability to mark reflected objects for use without default Java language access control checks
  • invokeMethods are checked firstAccessibleObjecttheoverrideProperty value:
    • Override defaults to false:
      • Indicates that permission invocation rules are required, and permissions need to be checked when invoking methods
      • It can also be set to true using setAccessible()
    • Override if true:
      • Indicates that permission rules are ignored and no permission check is required when a method is invoked
      • That is, any private method can be called, violating encapsulation
  • If the Override attribute is the default value false, further permission checks are performed:
  1. First using Reflection. QuickCheckMemberAccess (clazz, modifiers) method to check method for the public

1.1 if public methods, just skip this step 1.2 if not public methods, using Reflection. GetCallerClass () method calls this method for Class object, it is a native method

@CallerSensitive
	public static nativeClass<? > getCallerClass(); ------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------------------------------- - The JNI entry to the getCallerClass method can be found in the OpenJDK -Reflection.c

JNIEXPORT jclass JNICALL Java_sun_reflect_Reflection_getCallerClass__
(JNIEnv *env, jclass unused)
{
    returnJVM_GetCallerClass(env, JVM_CALLER_DEPTH); } ------------------------------------------------------------------------------------------------------------------------ --------------------------------------------------------------------------------------- - CPP VM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env,int depth))
  JVMWrapper("JVM_GetCallerClass");
  // Pre-JDK 8 and early builds of JDK 8 don't have a CallerSensitive annotation; or
  // sun.reflect.Reflection.getCallerClass with a depth parameter is provided
  // temporarily for existing code to use until a replacement API is defined.
  if(SystemDictionary::reflect_CallerSensitive_klass() == NULL || depth ! = JVM_CALLER_DEPTH) { Klass* k = thread->security_get_caller_class(depth);return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror());
  }
  // Getting the class of the caller frame.
  //
  // The call stack at this point looks something like this:
  //
  // [0] [ @CallerSensitive public sun.reflect.Reflection.getCallerClass ]
  // [1] [ @CallerSensitive API.method ]
  // [.] [ (skipped intermediate frames) ]
  // [n] [ caller ]
  vframeStream vfst(thread);
  // Cf. LibraryCallKit::inline_native_Reflection_getCallerClass
  for (int n = 0; ! vfst.at_end(); vfst.security_next(), n++) { Method* m = vfst.method();assert(m ! = NULL,"sanity");
    switch (n) {
    case 0:
      // This must only be called from Reflection.getCallerClass
      if(m->intrinsic_id() ! = vmIntrinsics::_getCallerClass) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(),"JVM_GetCallerClass must only be called from Reflection.getCallerClass");
      }
      // fall-through
    case 1:
      // Frame 0 and 1 must be caller sensitive.
      if(! m->caller_sensitive()) { THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), err_msg("CallerSensitive annotation expected at frame %d", n));
      }
      break;
    default:
      if(! m->is_ignored_by_security_stack_walk()) {// We have reached the desired frame; return the holder class.
        return (jclass) JNIHandles::make_local(env, m->method_holder()->java_mirror());
      }
      break; }}return NULL;
JVM_END
Copy the code
  1. Obtain the Class object caller and use the checkAccess method to perform a quick permission check. The checkAccess method is implemented as follows:
volatile Object securityCheckCache;

	void checkAccess(Class<? > caller, Class<? > clazz, Object obj,int modifiers) throws IllegalAccessException {
		if(caller == clazz){	// Quick check
			return;				// Permission is verified
		}
		Object cache = securityCheckCache;	/ / read volatileClass<? > targetClass = clazz;if(obj ! =null&& Modifier.isProtected(modifiers) && ((targetClass = obj.getClass()) ! = clazz)) {// Must match caller, one of the targetClasses
			if (cache instanceofClass[]) { Class<? >[] cache2 = (Class<? >[]) cache;if (cache2[1] == targetClass && cache[0] == caller) {
					return;		// The check is successful}}}else if (cache == caller) {
			return;				// The check is successful
		}
		slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
	}
Copy the code

A quick check is performed first. Once the Class is correct, the permission check passes. If it fails, a cache is created and checked in the middle

  • If all the above permission checks fail, a more detailed check is performed:
void slowCheckMemberAccess(Class<? > caller, Class<? > clazz, Object obj,intmodifiers, Class<? > targetClass) throws IllegalAccessException {
	Refelection.ensureMemberAccess(caller, clazz, obj, modifiers);
	// If successful, the cache is updated
	Object cache = ((targetClass == clazz) ? caller : newClass<? >[] {caller, targetClass}); securityCheckCache = cache; }Copy the code

With the Reflection. EnsureMemberAccess method to continue to check permissions. If the check passes, the cache is updated so that the next time the same class calls the same method, no permission check is performed. This is a simple caching mechanism. Since the JMM happens-before rule ensures that cache initialization can occur between write caches, both caches do not need to be declared volatile

  • This is the end of checking permissions. If it does not pass the check, an exception is thrown, if it passes the check, the next step is taken

Invoke the invoke method of MethodAccessor

  • Method. The invoke () not for reflection calls logic itself, but by sun. Refelect the MethodAccessor to deal with
  • Method object basic composition:
    • Each Java Method has one and only one Method object as root, which acts as the root object and is not visible to the user
    • When you create a Method object, the Method object you get in your code is equivalent to a copy or reference to it
    • The root object holds a MethodAccessor object, which is shared by all Method objects retrieved
    • MethodAccessor must be visible in memory
  • Root object and its declaration:
private volatile MethodAccessor methodAccessor;
/** * For sharing of MethodAccessors. This branching structure is * currently only two levels deep (i.e., one root Method and * potentially many Method objects pointing to it.) * * If this branching structure would ever contain cycles, deadlocks can * occur in annotation code. */
private Method  root;
Copy the code
  • MethodAccessor:
/** * This interface provides the declaration for * java.lang.reflect.Method.invoke(). Each Method object is * configured with a (possibly dynamically-generated) class which * implements this interface */
 public interface MethodAccessor {
 	// Matches specification in {@link java.lang.reflect.Method}
 	public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException;
 }
Copy the code

MethodAccessor is an interface that defines the invoke() method, and we can see the implementation class for MethodAccessor from Usage:

  1. sun.reflect.DelegatingMethodAccessorImpl
  2. sun.reflect.MethodAccessorImpl
  3. sun.reflect.NativeMethodAccessorImpl
  • The MethodAccess object that implements the calling logic has not been created before the invoke() Method of the corresponding Method object of the Java Method is called for the first time
  • On the first invocation, MethodAccessor is created and updated to root, and then methodAccessor.invoke () is called to complete the reflection call
/** * NOTE that there is no synchronization used here. * It is correct(though not efficient) to generate more than one MethodAccessor for a given Method. * However, avoiding synchronization will probably make the implementation more scalable. */

private MethodAccessor acquireMethodAccessor(a) {
	// First check to see if one has been created yet, and take it if so
	MethodAccessor tmp = null;
	if(root ! =null)
		tmp = root.getMethodAccessor();
	if(tmp ! =null) {
		methodAccessor = tmp;
	} else {
		tmp = reflectionFactory.newMethodAccessor(this);
		setMethodAccessor(tmp);
	} 
	return tmp;
} 
Copy the code
  • MethodAccessor instances are generated by manipulating the reflectionFactory object, which is declared in AccessibleObject:
/** * Reflection factory used by subclasses for creating field, * method, and constructor accessors. Note that this is called very early in the bootstrapping process. */
static final ReflectionFactory reflectionFactory = AccessController.doPrivileged(
													new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
Copy the code
  • Sun. Reflect. ReflectionFactory method:
public class ReflectionFactory {
	private static boolean initted = false;
	private static Permission reflectionFactoryAccessPerm = new RuntimePermission("reflectionFactoryAccess");
	private static ReflectionFactory soleInstance = new ReflectionFactory();
	// Provides access to package-private mechanisms in java.lang.reflect
	private static volatile LangReflectAccess langReflectAccess;

	/** * "Inflation" mechanism. Loading bytecodes to implement Method.invoke() and Constructor. * newInstance() currently costs 3-4x more than an invocation via native code for the first invocation (though subsequent invocations have been benchmarked to be over 20x faster) * Unfortunately this cost increases startup time for certain applications that use reflection intensively (but only once per class) to bootstrap themselves * To avoid this penalty we reuse the existing JVM entry points for the first few invocations of Methods and Constructors and then switch to the bytecode-based implementations */

	// Package-private to be accessible to NativeMethodAccessorImpl and NativeConstructorAccessorImpl
	private static noInflation = false;
	private static int inflationThreshold = 15;

	/ / generated MethodAccessor
	public MethodAccessor newMethodAccessor(Method method) {
		checkInitted();

		if(noInflation && ! ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {return new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),
										 method.getName(),
										 method.getParameterTypes(),
										 method.getReturnType(),
										 method.getExceptionTypes(),
										 method.getModifiers());
		} else {
			NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
			DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
			acc.setParent(res);
			returnres; }}/** * We have to defer full initialization of this class until after the static initializer is run since java.lang.reflect * Method's static initializer (more properly, that for java.lang.reflect.AccessibleObject) causes this class's to be run, before the system properties are set up */
	 private static void checkInitted(a) {
	 	if (initted) return;
	 	AccessController.doPrivileged(
            new PrivilegedAction<Void>() {
                public Void run(a) {
                /** * Tests to ensure the system properties table is fully initialized * This is needed because reflection code is called very early in the initialization process (before command-line arguments have been parsed and therefore these user-settable properties installed * We assume that if System.out is non-null then the System class has been fully initialized and that the bulk of the startup code has been run */
                 if (System.out == null) {
                        // java.lang.System not yet fully initialized
                        return null;
                    }
                    String val = System.getProperty("sun.reflect.noInflation");
                    if(val ! =null && val.equals("true")) {
                        noInflation = true;
                    }
                    val = System.getProperty("sun.reflect.inflationThreshold");
                    if(val ! =null) {
                        try {
                            inflationThreshold = Integer.parseInt(val);
                        } catch (NumberFormatException e) {
                            throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
                        }
                    }
                    initted = true;
                    return null; }}); }}Copy the code
  • The actualMethodAccessorThere are two versions of the implementation, one isJavaVersion, one isnativeVersion, both have their own features:
    • The method.invoke () and constructive.newinstance () methods are 3-4 times faster to adopt native methods than Java methods on initial startup
    • After startup, native methods consume additional performance and are slower than Java methods
    • The Version implemented in Java takes more time to initialize, but performs better over time
  • Here are the performance features that HotSpot optimizes:
    • Crossing native boundaries can be a hindrance to optimization
  • To minimize performance loss, the HotSpot JDK uses Inflation:
    • Java methods use native several times at first when they are called by reflection
    • When the number of reflection calls exceeds the threshold, a dedicated MethodAccessor implementation class is generated that generates the bytecode of its Invoke () method
    • Later reflection calls to the Java method will use the Java version
  • ReflectionFactory.newMethodAccessor()generateMethodAccessorObject logic:
    • Native version will generate NativeMethodAccessorImpl and DelegatingMethodAccessorImpl two objects
  • DelegatingMethodAccessorImpl:
/* Delegates its invocation to another MethodAccessorImpl and can change its delegate at run time */
class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
    private MethodAccessorImpl delegate;
    DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {
        setDelegate(delegate);
    }
    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
        return delegate.invoke(obj, args);
    }
    void setDelegate(MethodAccessorImpl delegate) {
        this.delegate = delegate; }}Copy the code

DelegatingMethodAccessorImpl object is a middle tier, convenient in native and Java version of the switch between MethodAccessor

  • Native version MethodAccessor Java statement: sun. Reflect. NativeMethodAccessorImpl
/* Used only for the first few invocations of a Method; afterward,switches to bytecode-based implementation */
class NativeMethodAccessorImpl extends MethodAccessorImpl {
    private Method method;
    private DelegatingMethodAccessorImpl parent;
    private int numInvocations;
    NativeMethodAccessorImpl(Method method) {
        this.method = method;
    }
    public Object invoke(Object obj, Object[] args)
        throws IllegalArgumentException, InvocationTargetException
    {
    	/* We can't inflate methods belonging to vm-anonymous classes because that kind of class can't be referred to by name, hence can't be found from the generated bytecode */
    	if(++numInvocations > ReflectionFactory.inflationThreshold() && ! ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) { MethodAccessorImpl acc = (MethodAccessorImpl)new MethodAccessorGenerator().
                    generateMethod(method.getDeclaringClass(),
                                   method.getName(),
                                   method.getParameterTypes(),
                                   method.getReturnType(),
                                   method.getExceptionTypes(),
                                   method.getModifiers());
            parent.setDelegate(acc);
        }
        return invoke0(method, obj, args);
    }
    void setParent(DelegatingMethodAccessorImpl parent) {
        this.parent = parent;
    }
    private static native Object invoke0(Method m, Object obj, Object[] args);
}
Copy the code
  • NativeMethodAccessorImpl. Every time the invoke () method is invoked, program calls the counter will increase 1, and see if it exceeds the threshold
  • If more than the call MethodAccessorGenerator. GenerateMethod () to generate the Java version of MethodAccessor implementation class
  • Change the MethodAccessor DelegatingMethodAccessorImpl reference to Java version
  • Through DelegatingMethodAccessorImpl. Invoke () call to is the realization of the Java version

JVM layer invoke0 method

  • The invoke0 method is a native method that calls the JVM_InvokeMethod function in HotSpot JVM:
JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0
(JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args)
{
    return JVM_InvokeMethod(env, m, obj, args);
}
Copy the code
  • openjdk/hotspot/src/share/vm/prims/jvm.cpp:
JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0))
  JVMWrapper("JVM_InvokeMethod");
  Handle method_handle;
  if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) {
    method_handle = Handle(THREAD, JNIHandles::resolve(method));
    Handle receiver(THREAD, JNIHandles::resolve(obj));
    objArrayHandle args(THREAD, objArrayOop(JNIHandles::resolve(args0)));
    oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL);
    jobject res = JNIHandles::make_local(env, result);
    if (JvmtiExport::should_post_vm_object_alloc()) {
      oop ret_type = java_lang_reflect_Method::return_type(method_handle());
      assert(ret_type ! = NULL,"sanity check: ret_type oop must not be NULL!");
      if (java_lang_Class::is_primitive(ret_type)) {
        // Only for primitive type vm allocates memory for java object.
        // See box() method.JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); }}return res;
  } else {
    THROW_0(vmSymbols::java_lang_StackOverflowError());
  }
JVM_END
Copy the code
  • A key part of the Reflection: : invoke_method: its/hotspot/SRC/share/vm/runtime/Reflection. CPP
oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) {
  oop mirror             = java_lang_reflect_Method::clazz(method_mirror);
  intslot = java_lang_reflect_Method::slot(method_mirror); bool override = java_lang_reflect_Method::override(method_mirror) ! =0;
  objArrayHandle ptypes(THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror)));
  oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror);
  BasicType rtype;
  if (java_lang_Class::is_primitive(return_type_mirror)) {
    rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL);
  } else {
    rtype = T_OBJECT;
  }
  instanceKlassHandle klass(THREAD, java_lang_Class::as_Klass(mirror));
  Method* m = klass->method_with_idnum(slot);
  if (m == NULL) {
    THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke");
  }
  methodHandle method(THREAD, m);
  return invoke(klass, method, receiver, override, ptypes, rtype, args, true, THREAD);
}
Copy the code

Object models for Java: Klass and OOP

Java version of the implementation

  • The Generation of the Java version of MethodAccessor is implemented using MethodAccessorGenerator
    Generator for sun.reflect.MethodAccessor and
    sun.reflect.ConstructorAccessor objects using bytecodes to
    implement reflection. A java.lang.reflect.Method or
    java.lang.reflect.Constructor object can delegate its invoke or
    newInstance method to an accessor using native code or to one
    generated by this class. (Methods and Constructors were merged
    together in this class to ensure maximum code sharing.)
Copy the code

Using the asm dynamically generated bytecode technology – sun. Reflect. ClassFileAssembler

Invoke summary

  • The invoke method procedure:

  • MagicAccessorImpl:
    • Java’s original security mechanism made it impossible for information between classes to be visible, but the JDK created a backdoor by creating a MagicAccessorImpl tag class that allows information between classes to be accessed by the JVM
/** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl and
    others, not because it actually implements an interface) is a
    marker class in the hierarchy. All subclasses of this class are
    "magically" granted access by the VM to otherwise inaccessible
    fields and methods of other classes. It is used to hold the code
    for dynamically-generated FieldAccessorImpl and MethodAccessorImpl
    subclasses. (Use of the word "unsafe" was avoided in this class's
    name to avoid confusion with {@link sun.misc.Unsafe}.) </P>
    <P> The bug fix for 4486457 also necessitated disabling
    verification for this class and all subclasses, as opposed to just
    SerializationConstructorAccessorImpl and subclasses, to avoid
    having to indicate to the VM which of these dynamically-generated
    stub classes were known to be able to pass the verifier. </P>
    <P> Do not change the name of this class without also changing the
    VM's code. </P> */
class MagicAccessorImpl {}Copy the code
  • @ CallerSensitive annotations
Summary: Improving the security of the JDK's method-Handle implementation by replacing the existing hand-Maintained list of faces caller-sensitive methods with a mechanism that accurately identifies such methods and allows their callers to be discovered reliably.Copy the code
/**
 * A method annotated @CallerSensitive is sensitive to its calling class,
 * via {@link sun.reflect.Reflection#getCallerClass Reflection.getCallerClass},
 * or via some equivalent.
 *
 * @author John R. Rose
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({METHOD})
public @interface CallerSensitive {
}
Copy the code
  • with@CallerSensitiveA method that is annotated knows from the start the object on which the method is called
    • You can determine the object on which this method is called without going through a series of checks
    • Is actually called sun. Reflect. Reflection. GetCallerClass method
  • ReflectionThe class is at frame 0 in the call stack
    • Sun. Reflect. Reflection. GetCallerClass () method returns the call stack in the first x frames starting from 0 frame of the class instance
    • This method provides a mechanism for identifying the Caller class to implement “Caller Sensitive” behavior
    • That is, it allows an application to change its own behavior based on the calling class or other classes in the calling stack

Reflective attention point

  • Reflection consumes additional system resources, so do not use reflection if you do not need to create an object dynamically
  • Reflection can ignore permission checks when calling methods. Can break encapsulation and cause security problems