Talk about the reflection mechanism of Java

The Java language allows indirect operations on Class through programmatic methods. After the Class file is loaded by the class loader, a meta-information object describing the Class structure will be formed in the JVM, through which the structure information of the Class can be known: Information such as constructors, properties, and methods.

1 Example

Suppose there is a class like this:

public class People {

    /**
     * 姓名
     */
    private String name;

    /**
     * 年龄
     */
    private int age;

    /**
     * 默认构造函数
     */
    public People() {
    }

    /**
     * 带参数的构造函数
     *
     * @param name
     * @param age
     */
    public People(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "People{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

In general, the way to create an instance is:

People people = new People("deniro", 22);

Next we manipulate the target class in a more general way through the Java reflection mechanism:

ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("net.deniro.springBoot.spring4.IoC.People");

//获取类的默认构造器对象,并使用这个对象实例化类
Constructor constructor = clazz.getDeclaredConstructor((Class[]) null);
People people2 = (People) constructor.newInstance();

//通过反射设置属性值
Method setNameMethod = clazz.getMethod("setName", String.class);
setNameMethod.invoke(people2, "Jack");
System.out.println("people2:" + people2);

This shows that we can call various elements of the Class programmatically, which is the same as calling the class directly through the constructor and method, but the former is an indirect call, and the latter is a direct call.

If we put this information in the configuration file, then we can use the reflection ability to write a general code to instantiate these classes and call the function. O(∩_∩)O haha~

2 Class Loader (ClassLoader)

2.1 Working mechanism

The class loader loads a class into the JVM by going through the following steps:

Loading steps

1. Load: Find and import Class files.
2. Link: Perform verification, preparation and analysis (optional) steps:
* Verification: Check the correctness of the data loaded into the Class file;
* Preparation: Allocate storage space for static variables of the class;
* Analysis: Convert symbolic references to into a direct reference;

3. Initialization: Initialize the static variables and static code blocks of the class.

class loader inheritance

JVM will generate three ClassLoader at runtime: root loader, ExtClassLoader (extended class loader) and AppClassLoader (application class loader). Note that the root loader is not a subclass of ClassLoader, it is written in C++, so it cannot be seen in Java, the root loader is responsible for loading the core class libraries of the JRE, such as rt.jar, charsets.jar and other classes under the JRE target library. Both ExtClassLoader and AppClassLoader are subclasses of ClassLoader.
* ExtClassLoader is responsible for loading the JAR class package in the JRE extension directory ext
* AppClassLoader is responsible for loading the class package under the Classpath path.

class loader hierarchy

There is a parent-child hierarchical relationship between the three class loaders, that is, the root loader is the parent loader of ExtClassLoader, and ExtClassLoader is the parent loader of AppClassLoader. By default, AppClassLoader is used to load application classes:

public class ClassLoaderTest {
    public static void main(String[] args) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        System.out.println("当前加载器:" + loader);
        final ClassLoader parent = loader.getParent();
        System.out.println("父加载器:" + parent);
        System.out.println("祖父加载器:" + parent.getParent());
    }
}

operation result:

当前加载器:sun.misc.Launcher$AppClassLoader@63961c42
父加载器:sun.misc.Launcher$ExtClassLoader@681a9515
祖父加载器:null

The grandfather ClassLoader is the root class loader and returns null because a handle to it cannot be obtained in Java.

The JVM uses the "full responsibility delegation mechanism" when loading classes. "Full responsibility" means that when a ClassLoader loads a class, unless another ClassLoader is explicitly specified, the classes that the class depends on and references are also controlled by the Loaded by this ClassLoader; the "delegation mechanism" means that the parent loader is first entrusted to find the target class, and only in the case of not being found, will the target class be found and loaded from its own classpath. This is from a security point of view, so as to avoid malicious tampering with the base class.


The java.lang.NoSuchMethodError error is generally caused by the overall responsibility delegation mechanism of the JVM. It may be caused by the placement of multiple different versions of class packages in the classpath.

Through the following methods, you can know the information from which JAR package a class is loaded from in the current environment:

public class ClassLocationUtils {

    /**
     * 获取某个类的所归属的类库路径
     *
     * @param clazz
     * @return
     */
    public static String source(final Class clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("clazz");
        }

        URL result = null;

        String name = clazz.getName().replace('.', '/').concat(".class");
        ProtectionDomain pd = clazz.getProtectionDomain();//获取保护域

        if (pd != null) {
            CodeSource source = pd.getCodeSource();
            if (source != null && source.getLocation() != null) {
                result = source.getLocation();
                if ("file".equals(result.getProtocol())) {
                    try {
                        if (result.toExternalForm().endsWith(".jar") || result.toExternalForm()
                                .endsWith(".zip")) {
                            result = new URL("jar:" + result.toExternalForm() + "!/" + name);
                        } else if (new File(result.getFile()).isDirectory()) {
                            result = new URL(result, name);
                        }
                    } catch (MalformedURLException e) {
                        throw new ClassLocationUtilsException("构造 URL 类", e);
                    }
                }
            }
        }

        if (result == null) {
            ClassLoader loader = clazz.getClassLoader();
            result = loader != null ? loader.getResource(name) : loader.getSystemResource(name);
        }

        return result.toString();
    }
}

Example of use:

System.out.println(ClassLocationUtils.source(StringUtils.class));

Output result:

jar:file:/F:/repo/m2/org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar!/org/apache/commons/lang3/StringUtils.class

2.2 Methods

method illustrate
Class loadClass(String name) The name parameter specifies the name of the class that the class loader needs to load, and must use the fully qualified class name. This method has an overloaded method loadClass(String name ,boolean resolve) , the resolve parameter tells the class loader whether it needs to resolve the class. Before initializing the class, you should consider the work of class resolution, but not all classes need to be resolved, if the JVM only needs to know whether the class exists or find out the superclass of the class, then it does not need to be resolved.
Class defineClass(String name, byte[] b, int off, int len) Convert the byte array of the class file to a java.lang.Class object inside the JVM. Byte arrays can be obtained from the local file system, remote network. name is the fully qualified class name corresponding to the byte array.
Class findSystemClass(String name) Load the Class file from the local file system. If the Class file does not exist in the local file system, a ClassNotFoundException will be thrown. This method is the default loading mechanism used by the JVM.
Class findLoadedClass(String name) Call this method to see if the ClassLoader has loaded a class. If installed

Enter, then return java.lang.Class object, otherwise return null. If you forcibly load an existing class, a linking error will be thrown.
ClassLoader getParent()| Get the parent loader of the class loader. Except for the root loader, all class loaders have one and only one parent loader. The parent loader of ExtClassLoader is the root loader, because the root loader is not Java written, so cannot be obtained, will return null.

You can write your own third-party classloaders to achieve some special needs. After the class file is loaded and parsed, there will be a corresponding java.lang.Class class description object in the JVM. Instances of this class have references to this class description object, and the class description object has a reference to the associated ClassLoader. :

The relationship between class instances, class description objects, and class loaders

Each class has a corresponding java.lang.Class object in the JVM, which provides a description of the class structure information. Arrays, enumerations, annotations, and basic Java types (such as int, double, etc.), and even void have corresponding Class objects. Class has no public constructor. Class objects are constructed by the JVM by calling the defineClass() method in the class loader when the class is loaded.

3 Reflection Mechanism

The Class reflection object describes the class semantic structure. We can obtain reflection objects of class elements such as constructors, member variables, and method classes from the Class object, and programmatically operate on the target class through these objects. These reflection object classes are defined in the java.reflect package.

1. Constructor: The constructor of the class reflects the class. Through the Class#getConstructors() method, all methods of the class can be obtained to reflect the class object array Method[]. In Java 5.0, the constructor reflection object with specific input parameters can also be obtained through getConstructor(Class… parameterTypes). One of the main methods of Constructor is newInstance(Object[] initargs), through which an instance of an object class can be created, equivalent to the new keyword. In Java 5.0 this method evolved into a more flexible form: newInstance(Object… initargs).

2. Method: The reflection class of the class method, through the Class#getDeclaredMethods() method, you can get all the methods of the class reflection class object array Method[]. In Java 5.0, you can get a method with a specific signature through getDeclaredMethod(String name, Class… parameterTypes), where name is the method name; Class… is a list of method input parameter types. The main method of Method is invoke(Object obj, Object[] args), where obj represents the target object of the operation; args is the method input parameter. In Java 5.0, this method has been adjusted to the form invoke(Object obj, Object… args). Also, Method has many methods for getting more information about a class method −

method illustrate
Class getReturnType() Gets the return value type of the method.
Class[] getParameterTypes() Gets an array of input parameter types for the method.
Class[] getExceptionTypes() Gets an array of exception types for the method.
Annotation[][] getParameterAnnotations() Get annotation information for a method, new in JDK 5.0.

3. Field: The reflection class of the member variables of the class, the class member variable reflection object array can be obtained through the Class#getDeclaredFields() method, and the member variable reflection object of a specific name can be obtained through Class#getDeclaredField(String name). The main method of the Field class is set(Object obj, Object value), where obj represents the target object of the operation, and sets the value for the member variable of the target object through value. If the member variable is a basic type, the user can use the value setting method with the type name provided in the Field class, such as setBoolean(Object obj, boolean value), setInt(Object obj, int value) and so on.

In addition, Java also provides the Package reflection class for packages, and in JDK 5.0 also provides the AnnotatedElement reflection class for annotations.

In a word, Java's reflection system ensures that all elements in the target class can be accessed programmatically. For private or protected member variables and methods, as long as the JVM security mechanism allows, they can also be invoked through reflection.

public class House {

    /**
     * 私有变量(只能在本类中被访问)
     */
    private String address;

    /**
     * 受保护的方法(只能在子类或者所在的包中被访问)
     */
    protected void decorate() {
        System.out.println("开始装修咯,所在地址:" + address);
    }


}

These private or protected variables and methods can be accessed through reflection:

public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
    ClassLoader loader = Thread.currentThread().getContextClassLoader();
    Class clazz = loader.loadClass("net.deniro.springBoot.spring4.IoC.House");

    House house = (House) clazz.newInstance();

    //设置 private 变量
    Field field = clazz.getDeclaredField("address");
    field.setAccessible(true);//取消访问检查
    field.set(house, "长安");

    //设置 protected 方法
    Method method = clazz.getDeclaredMethod("decorate");
    method.setAccessible(true);
    method.invoke(house, (Object[]) null);
}

When accessing private and protected member variables and methods, the Java language check must be canceled by the method setAccessible(boolean access), otherwise an IllegalAccessException will be thrown. If the JVM's security manager has set the corresponding security mechanism, then calling this method will throw a SecurityException exception.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324485239&siteId=291194637