Java basis of reflection (b)

Java reflection mechanism is a very powerful feature in many large-scale projects such as Spring, Mybatis can see the reflection of the figure. We can get through reflection during runtime type information object, use of this feature we can achieve factory mode and proxy mode design mode, but can also solve Java generics erased and other vexing issues. In this paper we set off from the practical point of view, to apply what reflection mechanism of Java.

Java reflection mechanism provides the following main features: a class object is constructed at runtime; determining a class has member variables and methods; invoke methods of an object; generating dynamic proxies. Reflection is the largest application framework.

Reflected many applications, many frameworks are useful to

The spring ioc / di reflection ... is
also a reflection ... calls between javaBean and jsp
between the struts and FormBean page ... also invoked by reflection ...
JDBC's classForName () is also a reflection ...
Hibernate's find (Class clazz) is reflected ...

Reflection basis

ps: This article requires readers have a certain degree of understanding of the mechanism of reflection API, if you have not been in contact, I suggest a look at the Quick Start official documents.

Before applying reflection, first of all let's look at how to get the reflection of an object corresponding class Class in Java, we have three ways to obtain a reflection type object.

By getClass method

In Java, each has a getClass Object method, we can obtain by the method getClass to the object class corresponding to the reflection:

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

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

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

Or we can directly use the .class:

Class<?> c = String.class;

In the beginning of the article we mentioned reflected a big advantage that we can allow the type of information that we get the object during operation, let's use an example to look at specific.

First, we create a new interface to A in typeinfo.interfacea the following package:

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

Then we pack in typeinfo.packageaccess below to create a new interface to C, C interfaces inherit from interfaces A, and in addition we also created several methods for testing, pay attention to the permissions of the following several 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 callHiddenMethod () method we used several new API, which getDeclaredMethod () The method for class Class name to refer to an object's method to get, then we pass the actual object by calling invoke () method can be trigger-related object method:

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);
    }
}

We can see from the output, whether it is public, default, protect or pricate method, by reflecting the class we are free to call. Of course, we are here just to show the power of reflection, in the actual development of such techniques do not advocate.

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

Here we have such a business scenario, we have a generic collection class List <Class <? Extends Pet >>, we need to count out this collection of each specific class of Pet how many. Due to the generics erased Java, similar attention List <? Extends Pet> practice certainly does not work, because the compiler do after static type checking, subject to the JVM will run during the collection are treated as Pet, but did We will not know what is represented Pet Cat or Dog, so the object type information during operation in fact all is lost.

In order to achieve our example above, let's define a few categories:

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, achieve Individual class is a little more complicated, we realized Comparable interface, re-customize the comparison rules category, if not quite understand, it does not matter, we have abstracted it out, so I do not understand that principle does not matter.

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));
    }
}

The following create an abstract class PetCreator, in the future we will be able to get a collection of related methods like direct by calling Pet arrayList (). Here we have to use the newInstance not mentioned above () method, which returns an instance of class Class class refers to the real, which is what does that mean? For example, the statement 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 we come to realize this above an abstract class, explain the following code in the code below, we declare two collections, allTypes and types, which we do allTypes it contains all the classes above stated, but we actually only two specific types namely Mutt and EgypianMau, so we really need new types out of the pets is the type contained in the future we will be able to get the type of yo types contained 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, the final number for us to achieve the set of relevant statistical category Pet TypeCounter class. Explain isAssignalbeFrom () method, which can determine whether a class is a subclass of a reflection type reflector or indirect subclass. The getSuperclass () to get the parent class name suggests is a reflection of the 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:
https://www.cnblogs.com/tartis/p/9299135.html

Published 42 original articles · won praise 0 · Views 934

Guess you like

Origin blog.csdn.net/mojiezhao/article/details/104163020