Share the detailed study of Java advanced reflection mechanism

What is reflection

Java reflection mechanism is to know all the properties and methods of any class in the running state; Any method of any object can be called; This function of dynamically obtaining and dynamically calling object methods is called java reflection mechanism.

Functions provided by java reflection mechanism:

  • Determine the class of any object at run time

  • Construct an object of any class at run time

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

  • Call the method of any object at run time

Reflection application scenario

Operate the attributes and methods restricted by access rights, such as private attributes and methods. For example, in android development, reading the sdk source code, you will find that there are many methods with hide annotation, which is hidden by google for the sake of security, and the application layer cannot be accessed directly, At this time, it can be called at runtime through reflection (android 9.0 adds the whitelist mechanism, and the interface in the blacklist cannot be called even through reflection).

android has many popular open source frameworks that apply custom annotations and reflections, such as EventBus and Retrofit;

Dynamic loading plug-in many large apps use the plug-in technology of dynamic loading and hot repair, and also use the reflection method to realize the Host's call to the plug-in. You can learn more about the plug-in open source framework, such as VirtualAppDroidPluginRePlugin

Java reflection mechanism

Reflection involves four core classes

  • java.lang.Class.java: class object;

  • java.lang.reflect.Constructor.java: constructor object of class;

  • java.lang.reflect.Method.java: method object of class;

  • java.lang.reflect.Field.java: attribute object of class;

#Working principle of reflection to correctly understand the java reflection mechanism, we must understand the class. Reflection is the operation of class class. When we write a java program (Java file), we will compile it first (generate a class file). When we run the program, the JVM will load the class file into memory and generate a class object. Through this class object, we can obtain the declaration and definition of the parent class, interface, method, member and construction method corresponding to the class object loaded into the virtual machine, We create objects in the form of new, which is actually created through these classes.

Reflection works by class java,Constructor.java,Method.java,Field.java these four classes dynamically access and modify the behavior and state of any class when the program is running.  

Common API s for reflection

1. Gets the Class object in the reflection

In Java API, there are three methods to obtain Class objects:

First, use Class Forname static method. When you know the full pathname of the Class, you can use this method to obtain the Class object.

Class clz = Class.forName("java.lang.String");

Second, use Class method. This method is only suitable for knowing the class of the operation before compilation.

Class clz = String.class;

Third, use the getClass() method of the class object.

String str = new String("Hello");
Class clz = str.getClass();

2. Create class objects by reflection

There are two main ways to create Class objects through reflection: through the newInstance() method of Class object and through the newInstance() method of Constructor object.

The first method: through the newInstance() method of the Class object.

Class clz = Stock.class;
Stock stock = (Stock)clz.newInstance();

The second method: through the newInstance() method of the Constructor object

Class clz = Stock.class;
Constructor constructor = clz.getConstructor();
Stock stock = (Stock)constructor.newInstance();

Creating a Class object through the Constructor object allows you to select a specific construction method, while only the default parameterless construction method can be used through the Class object. The following code calls a Constructor with parameters to initialize the Class object.

Class clz = Stock.class;
Constructor constructor = clz.getConstructor(String.class, String.class,String.class);
Stock stock= (Stock)constructor.newInstance("000001", "Ping An Bank","sz");

3. Get class properties, methods and constructors through reflection

We obtain properties through getFields() and getDeclaredFields() methods of Class object, and methods through getMethod() and getDeclaredMethod.

Methods with \ color{red}{Declared}Declared can only obtain the properties or methods of the class itself, including private ones. Methods without Declared cannot obtain private properties or methods, but include all public properties or methods of the parent class and itself.

Class clz = Stock.class;
Field[] fields = clz.getFields();//Include all public attributes of the parent class (excluding private)
Field[] declaredFields = clz.getDeclaredFields();//Own public and private attributes

Method[] methods = clz.getMethods();//Include all public methods of the parent class (excluding private)
Method[] declaredMethods = clz.getDeclaredMethods();//Own public and private methods

4. Get attribute value and execution method through reflection

Class stockClass= Stock.class;
Object stockObject = stockClass.newInstance();

Field marketField = stockClass.getDeclaredField("market");
marketField.setAccessible(true);//Private property or method, accessible=true must be set, otherwise the IllegalAccessException exception will be thrown
Log.i(TAG, "reflectPrivateField:" + marketField.get(stockObject));

 Method method = stockClass.getDeclaredMethod("getTrend", String.class);
 method.setAccessible(true);
 Object trend = method.invoke(stockObject, "5");
 Log.i(TAG, "reflectPrivateMethod:" + trend);

Complete code example

Prodct.java

public class Product {
    private String mCode;
    private String mName;
    public String type;

    public void setCode(String code) {
        mCode = code;
    }

    public void setName(String name) {
        mName = name;
    }

    public String getCode() {
        return mCode;
    }

    public String getName() {
        return mName;
    }

    public String toString() {
        return "Product{code=" + mCode + ",name=" + mName + ",type=" + type + "}";
    }
}

Stock.java

public class Stock extends Product {

    private String market = "sz";

    private int getTrend(String price) {
        if (TextUtils.isEmpty(price)) {
            return 0;
        } else if (price.contains("-")) {
            return -1;
        } else {
            return 1;
        }
    }

    @Call("do_sth_call")
    public void doSth() {
        Log.i("Stock", "do sth call");
    }

    public String toString() {
        return "Stock{code=" + getCode() + ",name=" + getName() + ",type=" + type + ",market=" + market + "}";
    }
}

Call.java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Call {
    String value();
}

ReflectTest.java

public class ReflectTest {
    public static final String TAG = "reflect";

    public static void testReflect() {
        reflectNewInstance();
        reflectPrivateField();
        reflectPrivateMethod();
        reflectAnnotation();
        reboot();
    }

    /**
     * create object
     */
    public static void reflectNewInstance() {
        try {
            Class<?> stockClass = Class.forName("com.demo.reflect.Stock");
            Object stockObject = stockClass.newInstance();
            Product product = (Product) stockObject;
            product.setCode("000001");
            product.setName("Ping An Bank");
            product.type = "1";
            Log.i(TAG, "reflectNewInstance stock=" + product.toString());
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * Reflection access private properties
     */
    public static void reflectPrivateField() {
        try {
            Class<?> stockClass = Class.forName("com.demo.reflect.Stock");
            Object stockObject = stockClass.newInstance();
            Field marketField = stockClass.getDeclaredField("market");
            marketField.setAccessible(true);
            Log.i(TAG, "reflectPrivateField:" + marketField.get(stockObject));
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * Reflection access private method
     */
    public static void reflectPrivateMethod() {
        try {
            Class<?> stockClass = Class.forName("com.demo.reflect.Stock");
            Object stockObject = stockClass.newInstance();
            Method method = stockClass.getDeclaredMethod("getTrend", String.class);
            method.setAccessible(true);
            Object trend = method.invoke(stockObject, "5");
            Log.i(TAG, "reflectPrivateMethod:" + trend);
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());
        }
    }

    /**
     * Reflection call annotation method
     */
    public static void reflectAnnotation() {
        try {
            Class<?> stockClass = Class.forName("com.demo.reflect.Stock");
            Object stockObject = stockClass.newInstance();
            Method[] methods = stockClass.getMethods();
            for (Method method : methods) {
                Call call = method.getAnnotation(Call.class);
                if (call != null && "do_sth_call".equals(call.value())) {
                    method.invoke(stockObject);
                }
            }
        } catch (Exception e) {

        }
    }

    // Restart the phone
    public static void reboot() {
        try {
            Class<?> cServiceManager = Class.forName("android.os.ServiceManager");
            Method mGetService = cServiceManager.getMethod("getService", String.class);
            Object oPowerManagerService = mGetService.invoke(null, Context.POWER_SERVICE);
            Class<?> cIPowerManagerStub = Class.forName("android.os.IPowerManager$Stub");
            Method mReboot = cIPowerManagerStub.getMethod("reboot", boolean.class, String.class, boolean.class);
            Method mAsInterface = cIPowerManagerStub.getMethod("asInterface", IBinder.class);
            Object oIPowerManager = mAsInterface.invoke(null, oPowerManagerService);
            mReboot.invoke(oIPowerManager, true, null, true);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

If the reflection executes the reboot method, an exception will be thrown, Java lang.reflect. Invocationtargetexception. The reason for the exception is Java lang.SecurityException: Neither user 10547 nor current process has android. permission. REBOOT. The reason is that there is no permission to reboot.

 Caused by: java.lang.SecurityException: Neither user 10547 nor current process has android.permission.REBOOT.
W/System.err:     at android.os.IPowerManager$Stub$Proxy.reboot(IPowerManager.java:1596)
W/System.err:     at com.android.server.power.PowerManagerService$BinderService.reboot(PowerManagerService.java:5662)

summary

This paper summarizes the functions and application scenarios of reflection, lists the commonly used API s, and gives code examples. Reflection has both advantages and disadvantages.

The advantage is that it provides a flexible mechanism to do whatever you want to do with classes. Some black technologies use reflection to realize functions that ordinary development cannot do. In addition, many frameworks use reflection to make development more convenient and efficient.

The disadvantage is that the performance of reflection operation method at runtime is slower than that of direct call, which generally can be ignored depending on how to reflect. Another problem is security. Modifying private properties and accessing private methods at will destroys the encapsulation of classes and may have potential logical hidden dangers. Another problem is that there may be adaptation problems when applied on Android. Therefore, we should fully understand the impact of these shortcomings before applying reflection.

Keywords: Java Android Design Pattern

Added by Hyperjase on Mon, 07 Feb 2022 13:48:07 +0200