Reflection of Java Foundation

Keywords: Java Spring Mybatis JSP

Java reflection mechanism is a very powerful function, which can be seen in many large projects such as spring and mybatis. Through the reflection mechanism, we can get the type information of the object during running. With this feature, we can implement design patterns such as factory pattern and agent pattern, and solve the vexing problems such as Java generic erasure. In this paper, we will apply the reflection mechanism of Java from the perspective of practical application.

Java reflection mechanism mainly provides the following functions: constructing an object of a class at runtime; judging the member variables and methods of a class; calling an object's methods; generating a dynamic proxy. The most reflective application is the framework.

There are many applications of reflection, and many frameworks are useful for

spring's ioc/di is also a reflection
The call between javaBean and jsp is also a reflection
Between the FormBean of struts and the page Also called by reflection
JDBC's classForName() is also a reflection
hibernate's find(Class clazz) is also a reflection

Reflection basis

p.s: This article requires readers to have a certain understanding of the API of reflection mechanism. If they haven't touched it before, it's recommended to first look at Quick Start in the official document.

Before applying the reflection mechanism, let's first look at how to obtain the reflection Class corresponding to an object. In Java, we have three methods to obtain the reflection Class of an object.

Through the getClass method

In Java, every Object has a getClass method. Through the getClass method, we can get the corresponding reflection class of this Object:

String s = "ziwenxie";
Class<?> c = s.getClass();
Through the forName method

We can also call the static method forName of Class class:

Class<?> c = Class.forName("java.lang.String");
Using.class

Or we can directly use. class:

Class<?> c = String.class;

At the beginning of this article, we mentioned that one of the great benefits of reflection is that it allows us to obtain the type information of objects during runtime. Let's take a look at it through an example.

First, we create A new interface A under the typeinfo.interface package:

package typeinfo.interfacea;
public interface A { void f(); }

Then we create A new interface C under the typeinfo.packageaccess package. Interface C inherits from interface A, and we also create several other methods for testing. Note that the permissions of the following methods are different.

package typeinfo.packageaccess;
import typeinfo.interfacea.A;
class C implements A {
    public void f() { System.out.println("public C.f()"); }
    public void g() { System.out.println("public C.g()"); }
    protected void v () { System.out.println("protected C.v()"); }
    void u() { System.out.println("package C.u()"); }
    private void w() { System.out.println("private C.w()"); }
}
public class HiddenC {
    public static A makeA() { return new C(); }
}

In the callHiddenMethod() method, we use several new API s, among which getDeclaredMethod() is used to get a method of the Class class reference object according to the method name, and then we can trigger the object's related methods by calling the invoke() method to pass in the actual object:

package typeinfo;
import typeinfo.interfacea.A;
import typeinfo.packageaccess.HiddenC;
import java.lang.reflect.Method;
public class HiddenImplementation {
    public static void main(String[] args) throws Exception {
        A a = HiddenC.makeA();
        a.f();
        System.out.println(a.getClass().getName());
        // Oops! Reflection still allows us to call g():
        callHiddenMethod(a, "g");
        // And even methods that are less accessible!
        callHiddenMethod(a, "u");
        callHiddenMethod(a, "v");
        callHiddenMethod(a, "w");
    }
    static void callHiddenMethod(Object a, String methodName) throws Exception {
        Method g = a.getClass().getDeclaredMethod(methodName);
        g.setAccessible(true);
        g.invoke(a);
    }
}

From the output results, we can see that whether it's public, default, protect or print methods, we can call them freely through reflection classes. Of course, we just want to show the powerful power of reflection here, which is not advocated in actual development.

public C.f()
typeinfo.packageaccess.C
public C.g()
package C.u()
protected C.v()
private C.w()

We have the following business scenario: we have a generic collection class list < class <? Extends Pet > > and we need to count the number of each specific Pet in this collection class. Because of Java's generic erasure, it's not possible to pay attention to the method similar to list <? Extends Pet >. After the compiler has done static type checking, the JVM will treat all objects in the collection as pets during the runtime, but it won't know whether the Pet represents Cat or Dog, so the type information of objects during the runtime is lost.

To implement the above example, we first define several classes:

public class Pet extends Individual {
    public Pet(String name) { super(name); }
    public Pet() { super(); }
}
public class Cat extends Pet {
    public Cat(String name) { super(name); }
    public Cat() { super(); }
}
public class Dog extends Pet {
    public Dog(String name) { super(name); }
    public Dog() { super(); }
}
public class EgyptianMau extends Cat {
    public EgyptianMau(String name) { super(name); }
    public EgyptianMau() { super(); }
}
public class Mutt extends Dog {
    public Mutt(String name) { super(name); }
    public Mutt() { super(); }
}

The above Pet class inherits from Individual. The implementation of Individual class is a little bit more complicated. We have implemented the Comparable interface and redefined the comparison rules of the class. If it's not clear, it doesn't matter. We have abstracted it, so it doesn't matter if we don't understand the implementation principle.

public class Individual implements Comparable<Individual> {
    private static long counter = 0;
    private final long id = counter++;
    private String name; // name is optional
    public Individual(String name) { this.name = name; }
    public Individual() {}
    public String toString() {
        return getClass().getSimpleName() + (name == null ? "" : " " + name);
    }
    public long id() { return id; }
    public boolean equals(Object o) {
        return o instanceof Individual && id == ((Individual)o).id;
    }
    public int hashCode() {
        int result = 17;
        if (name != null) {
            result = 37 * result + name.hashCode();
        }
        result = 37 * result + (int) id;
        return result;
    }
    public int compareTo(Individual arg) {
        // Compare by class name first:
        String first = getClass().getSimpleName();
        String argFirst = arg.getClass().getSimpleName();
        int firstCompare = first.compareTo(argFirst);
        if (firstCompare != 0) {
            return firstCompare;
        }
        if (name != null && arg.name != null) {
            int secendCompare = name.compareTo(arg.name);
            if (secendCompare != 0) {
                return secendCompare;
            }
        }
        return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));
    }
}

Next, we create an abstract Class, PetCreator. In the future, we can directly obtain the collection of related Pet classes by calling arrayList(). Here we use the newinstance () method that we didn't mention above. It will return the instance of the Class that the Class class really refers to. What does that mean? For example, declaring new Dog().getClass().newInstance() and direct new Dog() are equivalent.

public abstract class PetCreator {
    private Random rand = new Random(47);
    // The List of the different getTypes of Pet to create:
    public abstract List<Class<? extends Pet>> getTypes();
    public Pet randomPet() {
        // Create one random Pet
        int n = rand.nextInt(getTypes().size());
        try {
            return getTypes().get(n).newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }
    public Pet[] createArray(int size) {
        Pet[] result = new Pet[size];
        for (int i = 0; i < size; i++) {
           result[i] = randomPet();
        }
        return result;
    }
    public ArrayList<Pet> arrayList(int size) {
        ArrayList<Pet> result = new ArrayList<Pet>();
        Collections.addAll(result, createArray(size));
        return result;
    }
}

Next, let's implement the above abstract class and explain the following code. In the following code, we declare two collection classes, allTypes and types, among which allTypes contains all the classes we declared above. However, there are only two concrete types, namely Mutt and EgypianMau, so the pet we really need from new is only in types In the future, we can get the types contained in types by calling getTypes().

public class LiteralPetCreator extends PetCreator {
    @SuppressWarnings("unchecked")
    public static final List<Class<? extends Pet>> allTypes = Collections.unmodifiableList(
        Arrays.asList(Pet.class, Dog.class, Cat.class, Mutt.class, EgyptianMau.class));
    private static final List<Class<? extends Pet>> types = allTypes.subList(
        allTypes.indexOf(Mutt.class), allTypes.size());
    public List<Class<? extends Pet>> getTypes() {
        return types;
    }
}

The overall logic has been completed. Finally, we implement the TypeCounter class to count the number of related Pet classes in the collection. Explain the isassignalbeform() method, which can determine whether a reflection class is a subclass or an indirect subclass of a reflection class. And getSuperclass() just as the name implies is to get the parent class of a reflection class.

public class TypeCounter extends HashMap<Class<?>, Integer> {
    private Class<?> baseType;
    public TypeCounter(Class<?> baseType) {
        this.baseType = baseType;
    }
    public void count(Object obj) {
        Class<?> type = obj.getClass();
        if (!baseType.isAssignableFrom(type)) {
            throw new RuntimeException(
                obj + " incorrect type " + type + ", should be type or subtype of " + baseType);
        }
        countClass(type);
    }
    private void countClass(Class<?> type) {
        Integer quantity = get(type);
        put(type, quantity == null ? 1 : quantity + 1);
        Class<?> superClass = type.getSuperclass();
        if (superClass != null && baseType.isAssignableFrom(superClass)) {
            countClass(superClass);
        }
    }
    @Override
    public String toString() {
        StringBuilder result = new StringBuilder("{");
        for (Map.Entry<Class<?>, Integer> pair : entrySet()) {
            result.append(pair.getKey().getSimpleName());
            result.append("=");
            result.append(pair.getValue());
            result.append(", ");
        }
        result.delete(result.length() - 2, result.length());
        result.append("} ");
        return result.toString();
    }
}

Reference resources:
https://www.cnblogs.com/tartis/p/9299135.html

Published 41 original articles, won praise and 793 visitors
Private letter follow

Posted by les48 on Sun, 09 Feb 2020 03:53:45 -0800