Summary of practical experience of Java reflection mechanism, get started directly!

Posted by jimdelong on Mon, 03 Jan 2022 16:10:41 +0100

preface

I often use the reflection mechanism in practical projects, so I will take some summary notes on the learned reflection usage for future replay.

There is such a class:

package com.example.demo;
import com.alibaba.fastjson.annotation.JSONField;
public class User {
    private String name;
    @Value( value ="age_a")
    private String age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
     public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }
}

1, There are three ways to create a Class

1 - Class clazz = Class.forName("com.example.demo.User");


Note that the class name of forName("xxx") here needs to be a full name and an interface or class, otherwise it cannot be loaded.

2 - User user = new User();

Class clazz2 = user.getClass();

3 - Class clazz3 = User.class;

In the above three ways, you can get the Class object of Class User. Through Class, you can start playing reflection.

2, Reflection gets all properties and property types of the class

Class clazz = User.class;
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
    System.out.println("Property name:"+field.getName());
    System.out.println("Type of property:"+field.getGenericType().getTypeName());
}

Printout User properties and property types——

Attribute name: name

Type of attribute: Java lang.String

Attribute name: age

Type of attribute: Ava lang.String

After using reflection to obtain the field properties of the class, can reflection be used to create an object? The answer is yes.

For example, you can create an object by reflecting the field properties similar to the following code.

Map<String,Object> fileds = new HashMap<>();
fileds.put("name","Zhang San");
fileds.put("age","10");
Object o = User.class.newInstance();
 Field[] fields = o.getClass().getDeclaredFields();
 for (Field field : fields) {
     //After setting, private variables can be accessed by reflection
     field.setAccessible(true);
     //Assign values to attributes by reflection
     field.set(o,fileds.get(field.getName()));
 }
 User user1 = (User) o;
 System.out.println(user1.toString());

What scenarios might this be necessary? For example, the mapping of some internal data and external data fields can map the source data to the target data through a similar field reflection method, so as to obtain the target object that can be inserted into the database.

3, Reflection dynamically modifies the annotation value of class properties

Note that when setting the User class, we annotated one of the fields: @ value (value = "age_a"). This is a set value annotation. Since it is a set value, can it be dynamically modified according to different situations during code operation?

The annotations on the field are actually stored in a memberValues attribute, which is a map, which can be obtained in this way——

Field[] fields = User.class.getDeclaredFields();
for (Field field : fields) {
    //After setting, private variables can be accessed by reflection
    if ("age".equals(field.getName() )){
        field.setAccessible(true);
       //Get the InvocationHandler held by the proxy instance of annotation
       InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class));
       // Gets the memberValues field of InvocationHandler
        Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
        memberValues.setAccessible(true);
        Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
        System.out.println(values);
    }
}

debug break point, you can see——

This map < string, Object > stores all attribute values in the @ annotation. Here, @ value has only one value attribute——

public @interface Value {
    String value();
}

If you change it to something like @ JSONField(name = "age_a"), slightly modify the code above, such as:

Field[] fields = User.class.getDeclaredFields();
for (Field field : fields) {
    if ("age".equals(field.getName() )){
        field.setAccessible(true);
          InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(JSONField.class));
		......
    }
}

@The internal properties of the JSONField annotation are as follows——

Running the code just now, you can see that all the attributes and corresponding attribute values in this annotation are obtained and stored in map < string, Object >.

At this point, let's go back to the previous question. What should we do to dynamically change the value of this annotation?

In fact, it is very simple. You only need to set the value directly, for example——

InvocationHandler invocationHandler = Proxy.getInvocationHandler(field.getAnnotation(Value.class));
Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
memberValues.setAccessible(true);
Map<String, Object> values = (Map<String, Object>) memberValues.get(invocationHandler);
values.put("value","new_age");
memberValues.setAccessible(false);

Just note that the key here needs to correspond to the attribute value in the annotation.

4, Method and calling method of reflection acquisition class

Object o=User.class.newInstance();
//Get the setAge method of the User through reflection, followed by string Class represents the parameter type of this setAge method. If there are multiple, they will be listed in order
//Meanwhile, for other types, such as list and long, it is list class,Long. class
 Method m =  (Method) o.getClass().getMethod("setAge",String.class);
 m.invoke(o,"name");
 User user = (User) o;
 System.out.println(user);

It can be seen from the print that the age has been set to name, indicating that the setAge call was successful.

There are many such use scenarios in agents.

Finally, an encapsulation tool for converting Map into object is realized through reflection——

public Object MapToObject(Object object,Map<String, Object> map) throws IllegalAccessException {
        Class cla =  object.getClass();
        Field[] fields = cla.getDeclaredFields();
        for(Field field:fields){
            field.setAccessible(true);
            if("serialVersionUID".equals(field.getName()))continue;
            if(map.get(field.getName())!=null) {
                Object value=map.get(field.getName());
                value=convertValType(value,field.getType());
                field.set(object, value);
            }
        }
        return object;
    }


    private static Object convertValType(Object value, Class<?> fieldTypeClass) {
        Object o = null;
        if (Long.class.getName().equals(fieldTypeClass.getName())
                || long.class.getName().equals(fieldTypeClass.getName())) {
            o = Long.parseLong(value.toString());
        } else if (Integer.class.getName().equals(fieldTypeClass.getName())
                || int.class.getName().equals(fieldTypeClass.getName())) {
            o = Integer.parseInt(value.toString());
        } else if (Float.class.getName().equals(fieldTypeClass.getName())
                || float.class.getName().equals(fieldTypeClass.getName())) {
            o = Float.parseFloat(value.toString());
        } else if (Double.class.getName().equals(fieldTypeClass.getName())
                || double.class.getName().equals(fieldTypeClass.getName())) {
            o = Double.parseDouble(value.toString());
        } else {
            retVal = o;
        }
        return retVal;
    }

Finally, thank you for your watching and support. It's best to pay attention to the collection three times in a row. I wish you a good interview, promotion and salary increase, and promotion and salary increase as soon as possible!

Topics: Java Class reflection