Do you really understand reflection?

Posted by s2r on Fri, 21 Jan 2022 05:02:53 +0100

1. What is reflection

1. First knowledge reflex

When I first started to learn reflection, I was confused. This thing is really "abstract mother opens the door to abstract - abstract is home."

Why do you need to get the Class object before creating an object? Isn't that unnecessary? Isn't it easier for me to new directly?

What is the program runtime to get the properties and methods of a class? Usually, the code is modified after the program compilation error. Why should I consider the running state of the program?

I don't usually use it for development. What's the use of learning this thing?

Later, after learning annotation, spring, spring MVC and other technologies, I found that reflection is everywhere.

2.JVM loading class

The java program we write should be run in the JVM, so to learn reflection, we first need to understand the process of loading classes in the JVM.

1. We wrote it java files are called source code.

2. We write a main method in a class, and then click the run button of IDEA. When the JVM runs, it will trigger the javac instruction of jdk to compile the source code into Class file, which is also called bytecode file.

3. The JVM class loader (you can understand it as a tool) obtains the binary byte stream of a class through the fully qualified name of the class, and then loads the class file into the method area of the JVM.

4. Load a Class loader When the Class file is to the method area, a unique Class object will be generated in the heap. This Class contains the member variables, construction methods and member methods of this Class.

5. This Class object will create an object instance corresponding to this Class.

So on the surface, you create a new object. In fact, when the JVM runs the program, it is the Class object of this Class that really helps you create the object.

In other words, reflection is actually that the JVM encapsulates all classes you create into a unique Class object when running the program. This Class object contains properties, constructor methods, and member methods. When you get the Class object, you can get these three things.

After you get the attribute of the reflection Class, you can get the attribute name, attribute Class and attribute value of the object, and set the value for the attribute.

After you get the Class constructor, you can create the object.

After you get the member method of the reflection (Class), you can execute the method.

3. The concept of reflection

java reflection mechanism is to know all the properties and methods of any class in the running state of the program; For any object, you can call any of its methods and properties; This function of dynamically obtaining information and dynamically calling object methods is called the reflection mechanism of java language.

Knowing the process of loading classes by JVM, I believe you should have a deeper understanding of the concept of reflection.

Reflection: JVM runtime -- > Java file -- > Class file -- > class object -- > creates an object instance and operates the properties and methods of the instance

Next, I'll talk about related classes and common methods in reflection.

2. Class object

Get Class object

Create a User class first:

public class User {
    private  String name = "Know you";
    public String sex = "male";

    public User() {
    }

    public User(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    public void eat(){
        System.out.println("People want to eat!");
    }

    private void run(){
        System.out.println("People want to run!");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

There are three ways to get Class objects:

1. Class.forName("full class name")

Full class name: package name + class name

Class userClass = Class.forName("com.xxl.model.User");

2. Class name class

Class userClass = User.class;

3. Object getClass()

User user = new User();
Class userClass = user.getClass();

Although there are three ways to obtain Class objects, we generally use the first method above.

After we get the Class object, we can operate the methods related to it.

3. Get class name

1. Get the complete class name: package name + class name

getName()

Class userClass = Class.forName("com.xxl.model.User");
String name = userClass.getName();
System.out.println(name);

Print results:

2. Get simple class name: excluding package name

getSimpleName()

Class userClass = Class.forName("com.xxl.model.User");
String simpleName = userClass.getSimpleName();
System.out.println(simpleName);

Print results:

4. Properties

4.1 get attributes

1. Get all public attributes: public modifier

getFields()

Class userClass = Class.forName("com.xxl.model.User");
Field[] fields = userClass.getFields();
for (Field field : fields) {
    System.out.println(field);
}

Print results:

2. Get a single public attribute

getField("property name")

Class userClass = Class.forName("com.xxl.model.User");
Field field = userClass.getField("sex");
System.out.println(field);

Print results:

3. Get all attributes: public + Private

getDeclaredFields()

Class userClass = Class.forName("com.xxl.model.User");
Field[] fields = userClass.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field);
}

Print results:

4. Get a single attribute: public or private

Getdeclaraedfield ("property name")

Class userClass = Class.forName("com.xxl.model.User");
Field nameField = userClass.getDeclaredField("name");
Field sexField = userClass.getDeclaredField("sex");
System.out.println(nameField);
System.out.println(sexField);

Print results:

4.2 operation attributes

1. Get attribute name

getName()

Class userClass = Class.forName("com.xxl.model.User");
Field nameField = userClass.getDeclaredField("name");
System.out.println(nameField.getName());

Print results:

2. Get attribute type

getType()

Class userClass = Class.forName("com.xxl.model.User");
Field nameField = userClass.getDeclaredField("name");
System.out.println(nameField.getType());

Print results:

3. Get attribute value

get(object)

Class userClass = Class.forName("com.xxl.model.User");
Field nameField = userClass.getDeclaredField("sex");
User user = new User();
System.out.println(nameField.get(user));

Print results:

Note: the value of the private attribute cannot be obtained directly through reflection, but the value of the private attribute can be obtained by modifying the access entry.

Set allowed access to private properties:

field.setAccessible(true);

For example:

Class userClass = Class.forName("com.xxl.model.User");
Field nameField = userClass.getDeclaredField("name");
nameField.setAccessible(true);
User user = new User();
System.out.println(nameField.get(user));

Printing method:

4. Set attribute value

set(object, "attribute value")

Class userClass = Class.forName("com.xxl.model.User");
Field nameField = userClass.getDeclaredField("name");
nameField.setAccessible(true);
User user = new User();
nameField.set(user,"zhang wuji");
System.out.println(nameField.get(user));

Print results:

5. Construction method

1. Get all public construction methods

getConstructors()

Class userClass = Class.forName("com.xxl.model.User");
Constructor[] constructors = userClass.getConstructors();
for (Constructor constructor : constructors) {
    System.out.println(constructor);
}

Print results:

2. Obtain the construction method matching the parameter type

Getconstructor (parameter type)

Class userClass = Class.forName("com.xxl.model.User");
Constructor constructor = userClass.getConstructor(String.class, String.class);
System.out.println(constructor);

Print results:

6. Membership method

6.1 method of obtaining members

1. Access to all public methods

getMethods()

Class userClass = Class.forName("com.xxl.model.User");
Method[] methods = userClass.getMethods();
for (Method method : methods) {
    System.out.println(method);
}

Print results:

We found that in addition to the custom public methods, there are also public methods inherited from the Object class.

2. Get a public method

getMethod("method name", parameter type)

Class userClass = Class.forName("com.xxl.model.User");
Method method = userClass.getMethod("setName", String.class);
System.out.println(method);

Print results:

3. Get all methods: public + Private

getDeclaredMethods()

Class userClass = Class.forName("com.xxl.model.User");
Method[] declaredMethods = userClass.getDeclaredMethods();
for (Method method : declaredMethods) {
    System.out.println(method);
}

Print results:

4. Get a method: public or private

Getdeclaraedmethod ("method name", parameter type)

Class userClass = Class.forName("com.xxl.model.User");
Method method = userClass.getDeclaredMethod("run");
System.out.println(method);

Print results:

6.2 executive member method

invoke(object, "method parameter")

Class userClass = Class.forName("com.xxl.model.User");
Method method = userClass.getDeclaredMethod("eat");
User user = new User();
method.invoke(user);

Print results:

Note: private member methods cannot be executed directly through reflection, but access can be set.

Set the allowed private methods:

method.setAccessible(true);

7. Notes

1. Judge whether a class or method contains an annotation

isAnnotationPresent(Annotation name.class)

For example:

Class userClass = Class.forName("com.xxl.model.User");
if(userClass.isAnnotationPresent(Component.class)){
    Component annotation = (Component)userClass.getAnnotation(Component.class);
    String value = annotation.value();
    System.out.println(value);
};

2. Get comments

getAnnotation(Annotation name.class)

For example:

Class userClass = Class.forName("com.xxl.model.User");
// Get annotation on class
Annotation annotation1 = userClass.getAnnotation(Component.class);
Method method = userClass.getMethod("eat");
// Gets an annotation on a method
Annotation annotation2 = userClass.getAnnotation(Component.class);

8. Create an instance of the class

1. Instantiate objects through Class

Class.newInstance()

Class userClass = Class.forName("com.xxl.model.User");
User user = (User)userClass.newInstance();
System.out.println("full name:"+user.getName()+" Gender:"+user.getSex());

Print results:

2. Instantiate objects through construction methods

constructor. Newinstance (parameter value)

Class userClass = Class.forName("com.xxl.model.User");
Constructor constructor = userClass.getConstructor(String.class, String.class);
User user = (User)constructor.newInstance\("Li Shiqing", "female"\);
System.out.println("full name:"+user.getName()+" Gender:"+user.getSex());

Print results:

9. Reflection cases

One day, the technical director said to Zhang San, "Zhang San, I heard that you have learned reflection recently. Then you design an object factory class and show it to me."

Zhang San thought, "Oh, it's almost the new year. The leader is going to give me a raise. I'll do well this time."

Five minutes later, Zhang San submitted the code:

public class ObjectFactory {
    
    public static User getUser() {
        
        User user = null;
        try {
            Class userClass = Class.forName("com.xxl.model.User");
            user = (User) userClass.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return user;
    }

    public static UserService getUserService() {
        UserService userService = null;
        try {
            Class userClass = Class.forName("com.xxl.service.impl.UserServiceImpl");
            userService = (UserService) userClass.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
        return userService;
    }
}

The technical director glanced at the code and said to Zhang San, "there are two problems in your factory class."

1. There is a lot of redundancy in the code. If there are 10000 classes, do you want to write 10000 static methods?

2. Code coupling is too high. If the package path of these classes changes, will it be a problem if you use forName() to get the Class object? You have to manually change the code one by one, and then compile, package and deploy it.. Don't you find it troublesome?

"Spread your thinking and think about whether you can only design a static class and create objects with reflection by passing parameters. The passed parameters should reduce the coupling with the factory class. By the way, you can refer to the way JDBC obtains database connection parameters."

Zhang Sanyi Listened: "it's worthy of being the director. Be enlightened! Wait for me for 10 minutes."

After 10 minutes, Zhang San submitted the code again:

object.properties

user=com.xxl.model.User
userService=com.xxl.service.impl.UserServiceImpl

ObjectFactory

public class ObjectFactory {

    private static Properties objectProperty = new Properties();

    // Static methods are executed at class initialization and only once
    static{
        try {
            InputStream inputStream = ObjectFactory.class.getResourceAsStream("/object.properties");
            objectProperty.load(inputStream);
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static Object getObject(String key){
        Object object = null;
        try {
            Class objectClass = Class.forName(objectProperty.getProperty(key));
            object = objectClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return object;
    }
}

Test method:

@Test
  void testObject() {
      User user = (User)ObjectFactory.getObject("user");
      UserService userService = (UserService)ObjectFactory.getObject("userService");
      System.out.println(user);
      System.out.println(userService);
  }

Execution results:

After seeing this, the director nodded again and again, smiled and said to Zhang San: "using the properties file to store the fully qualified name of the class reduces the coupling degree of the code, and using reflection to create objects by passing parameters reduces the redundancy of the code. This time, it can be changed“

"Well, the project will go online tonight. Let's have dinner first and fix the bug later."

Zhang San: "...... good director."

10. Function of reflection

We have more or less heard that reflection is used when designing frameworks. For example, Spring IOC uses factory pattern and reflection to create objects, and the bottom layer of BeanUtils also uses reflection to copy attributes. So reflection is everywhere.

Although we hardly use reflection in our daily development, we must understand the principle of reflection, because it can help us understand the principle of framework design.

 

Topics: Java Spring Back-end architecture intellij-idea