Java Reflection Technology and Its Application

I have read a lot of materials and want to see a relatively easy-to-understand understanding of java reflection mechanism, but it may be a little difficult to understand a technology from the concept, so I will post the theoretical knowledge first, and then I will understand it slowly .

Reflection is that in the running state, for any class, you can know all the properties and methods of this class; for any object, you can call any of its methods and properties; this kind of dynamic information acquisition and dynamic invocation of object methods It is called reflection mechanism of java language. --"Baidu Encyclopedia"

注意: To understand the reflection mechanism of java, you must first understand the following basic concepts:
运行时, 编译时, 编译型, 解释型, 类加载器, 动态加载类and other related concepts.

1. The use of Class

  1. Conceptual understanding
    In Java, each class has a corresponding Class object. That is to say, when we write a class, after the compilation is completed, in the generated .class file, a Class object will be generated to represent the type information of this class.
  2. The way to obtain a Class instance
    cannot directly create an instance object of a Class, because the constructor of the Class class is private, and only the jvm can create it.
    (1) Use the object to call the getClass() method to obtain the Class instance of the object;
    (2) Use the static method forName() of the Class class to obtain a Class instance with the name of the class. The source code is as follows;
    @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

(3) Use the .class method to obtain the Class instance. For the encapsulation class of the basic data type, you can also use TYPE to obtain the Class instance of the corresponding basic data type.

To sum up, in fact, each class created in our code is an object, but it is an instance object of the Class class, which we call the class type of the class. And a class can only be an instance object of the Class class, that is, the obtained class types are the same

So, how to create an instance of Class?
First of all, the process should be understood that after the source file is compiled (javac.exe), one or more .class files are obtained. After the .class file is run (java.exe), the class needs to be loaded (through the JVM's class loader) and recorded in the memory cache. Each .class file placed in the cache is an instance of a Class! Below are three ways to create a Class instance.

Examples are as follows:

public class ReflectTest {

    public void display(){
        System.out.println("Hello World!");
    }
    public static void main(String[] args){
        ReflectTest reflectTest = new ReflectTest();
        Class clazz1 = reflectTest.getClass();
        Class clazz2 = ReflectTest.class;
        Class clazz3 = null;
        try {
            clazz3 = Class.forName("com.william.test.ReflectTest");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(clazz1==clazz2);
        System.out.println(clazz1==clazz3);
        System.out.println(clazz2==clazz3);
    }
}

output:
true
true
true

In fact, there is another way to create a Class instance, which is through the class loader, as follows:

@Test
public void LoadClassTest(){
    ClassLoader loader = this.getClass().getClassLoader();
    Class clazz4 = null;
    try {
        clazz4 = loader.loadClass("com.william.test.ReflectTest");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    System.out.println(clazz4);
}

output:
class com.william.test.ReflectTest
  • How to create an instance of a class based on the class type?
//通过类的类型创建该类的实例对象
ReflectTest reflectTest1 = null;
try {
    reflectTest1 = (ReflectTest) clazz1.newInstance();//需要类有无参的构造方法
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}
reflectTest.display();

output:
Hello World!
  1. Dynamically load classes
    Class.forName("类的全称")

    • Represents the class type of the class, and also represents the dynamically loaded class
    • Distinguish the compilation mentioned above, run
    • Classes loaded at compile time are statically loaded classes, and classes loaded at runtime are
      dynamically loaded classes. For the example code of dynamically loaded classes, please refer to: http://www.imooc.com/video/3733
  2. java get method information

/**
 *打印类的信息,包括类的成员函数,成员变量
 */
public class ClassUtil {
    /**
     * 获取成员函数
     * object 该对象所属类的信息
     *
     * @param object
     */
    public static void printMethodMessage(Object object) {
        //要获取类的信息,首先要获取类的类类型
        Class clazz = object.getClass();//传递的是哪个子类的对象, clazz就是该子类的类类型
        //获取类的名称
        System.out.println("类的名称是:" + clazz.getName());

        /**
         * Method类,方法对象
         * 一个成员方法就是一个Method对象
         * getMethods()方法获取的是所有的public的函数,包括父类继承而来的
         * getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
         */
        Method[] methods = clazz.getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            //1. 获取注解
            Annotation[] ann = methods[i].getAnnotations();
            for (Annotation a : ann) {
                System.out.println(a);
            }

            //2. 获取权限修饰符
            String str = Modifier.toString(methods[i].getModifiers());
            System.out.print(str + " ");

            //3. 得到方法的返回值类型的类类型
            Class returnType = methods[i].getReturnType();
            System.out.print(returnType.getName() + " ");

            //4. 得到方法的名称
            System.out.print(methods[i].getName() + "(");

            //5.获取参数类型-->得到的是参数列表的类型的类类型
            Class[] paramTypes = methods[i].getParameterTypes();
            //解析数组
            for (int j = 0; j < paramTypes.length; j++) {
                if (j == 1 || j == paramTypes.length - 1) {
                    System.out.print(paramTypes[j].getName() + " args" + j);
                } else {
                    System.out.print(paramTypes[j].getName() + " args" + j + ",");
                }
            }
            System.out.print(")");

            //6.获取异常类型
            Class[] exps = methods[i].getExceptionTypes();
            if (exps.length != 0) {
                System.out.print(" throws ");
            }
            for (int k = 0; k < exps.length; k++) {
                System.out.print(exps[k].getName() + " ");
            }
            System.out.println();
        }
    }   
}

class MethodTest {
    private String name;
    //注解类型在java之注解开发章节讲解过的
    @MyAnnotation(wd = WeekDay.MON,id = 4)
    public int age;
    static String desc = "这是一个人";
    public MethodTest() {
    }

    private MethodTest(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Deprecated
    public void print(int a, int b) throws Exception {
        System.out.println(a + b);
    }

    public void print(String a, String b) {
        System.out.println(a.toUpperCase() + "," + b.toLowerCase());
    }

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

//调用
MethodTest methodTest = new MethodTest();
ClassUtil.printMethodMessage(methodTest);


output:
类的名称是:com.william.test.MethodTest
public java.lang.String toString()
@java.lang.Deprecated()
public void print(int args0,int args1) throws java.lang.Exception 
public void print(java.lang.String args0,java.lang.String args1)

To obtain method information, the method class array is mainly used to accept the return value of the getMethods() method, and then traverse and parse.

  1. Obtaining member variable information
    Obtaining member variable information is similar to obtaining method information, except that the Field class array is used to receive the return value of the getFieldss() method, and then parse it. code show as below:
/**
 * 获取成员变量
 */
public static void printFieldMessage(Object object) {
    //要获取类的信息,首先要获取类的类类型
    Class clazz = object.getClass();//传递的是哪个子类的对象, clazz就是该子类的类类型
    //获取类的名称
    System.out.println("类的名称是:" + clazz.getName());

    /**
     * 成员变量也是对象
     * java.lang.reflect.Field
     * Field类封装了关于成员变量的操作
     * getFields()方法获取的是所有的public的成员变量的信息
     * getDeclaredFields获取的是该类自己声明的成员变量的信息
     */

    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        //获取每个属性的权限修饰符
        int i = field.getModifiers();
        String str = Modifier.toString(i);
        //得到成员变量的类型的类类型
        Class fieldType = field.getType();
        String typeName = fieldType.getName();
        //得到成员变量的名称
        String fieldName = field.getName();
        System.out.println(str + " "+ typeName + " " + fieldName);
    }

}
//调用
String str = "Hello World!";
ClassUtil.printFieldMessage(str);

output:
类的名称是:java.lang.String
类的名称是:java.lang.String
private final [C value
private int hash
private static final long serialVersionUID
  1. get constructor
/**
 * 获取对象的构造函数的信息
 *
 * @param object
 */
public static void printConMessage(Object object) {
    //要获取类的信息,首先要获取类的类类型
    Class clazz = object.getClass();//传递的是哪个子类的对象, clazz就是该子类的类类型
    //获取类的名称
    System.out.println("类的名称是:" + clazz.getName());

    /**
     * 构造函数也是对象
     * java.lang.Constructor中封装了构造函数的信息
     * getConstructors获取所有的public的构造函数
     * getDeclaredConstructors得到所有的构造函数
     */
    Constructor[] constructors = clazz.getConstructors();
    for (Constructor constructor : constructors) {
        System.out.print(constructor.getName() + "(");
        //获取构造函数的参数列表--->得到的是参数列表的类类型
        Class[] paramTypes = constructor.getParameterTypes();
        for (Class clazz1 : paramTypes) {
            System.out.print(clazz1.getName() + ",");
        }
        System.out.println(")");
    }

}

//调用
String str = "Hello World!";
ClassUtil.printConMessage(str);

output:
类的名称是:java.lang.String
java.lang.String([B,int,int,)
java.lang.String([B,java.nio.charset.Charset,)
java.lang.String([B,java.lang.String,)
java.lang.String([B,int,int,java.nio.charset.Charset,)
java.lang.String([B,int,int,java.lang.String,)
java.lang.String(java.lang.StringBuilder,)
java.lang.String(java.lang.StringBuffer,)

2. Reflection of the method

  1. Method reflection
    1) How to obtain a
    method The name of the method and the list of parameters in the method can uniquely determine a method
    2) The operation of method reflection
    method.invoke (object, list in parameters)
    Sample code:
public class MethodReflect {
    public static void main(String[] args){
        //要获取print(int,int)方法 1. 要获取一个方法就是获取类的信息,获取类的信息首先要获取类的类类型
        MethodTest methodTest = new MethodTest();
        Class clazz = methodTest.getClass();

        try {
            //Method  method = clazz.getMethod("print",new Class[]{int.class,int.class});
            Method method = clazz.getMethod("print",int.class,int.class);

            //以前的方法的方法调用
            //methodTest.print(10,20);

            //方法的反射操作
            //方法如果没有返回值返回null,有返回值返回具体的返回值
            //method.invoke(methodTest,new Object[]{10,20});
            Object o = method.invoke(methodTest,10,20);
            System.out.println("============================");
            //获取方法print(String,String)
            Method method1 = clazz.getMethod("print",String.class,String.class);
            //用方法进行反射操作
            o = method1.invoke(methodTest,"hello","WORLD!");


        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class MethodTest {
    private String name;
    public int age;
    static String desc = "这是一个人";
    public MethodTest() {
    }

    private MethodTest(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void print(int a, int b) {
        System.out.println(a + b);
    }

    public void print(String a, String b) {
        System.out.println(a.toUpperCase() + "," + b.toLowerCase());
    }

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

output:
30
============================
HELLO,world!

3. Reflection of member variables

Examples are as follows:

//成员变量的反射
Class clazz1;
try {
    clazz1 = Class.forName("com.william.test.MethodTest");
    Object object = clazz1.newInstance();
    MethodTest methodTest1 = (MethodTest) object;
    //调用private属性
    Field field1 = clazz1.getDeclaredField("name");
    field1.setAccessible(true);
    field1.set(methodTest1,"william");
    System.out.println(methodTest1.toString());
    //调用public的属性
    Field field2 = clazz1.getField("age");
    field2.set(methodTest1,9);
    System.out.println(methodTest1.toString());
    //调用static属性
    Field field3 = clazz1.getDeclaredField("desc");
    System.out.println(field3.get(MethodTest.desc));
    //System.out.println(field3.get(null));
} catch (Exception e) {
    e.printStackTrace();
}
output:
MethodTest{name='william', age=0}
MethodTest{name='william', age=9}
这是一个人

4. Constructor reflection

Examples are as follows:

//构造函数的反射
Class clazz3 = MethodTest.class;
try {
    Constructor constructor = clazz3.getDeclaredConstructor(String.class,int.class);
    constructor.setAccessible(true);
    MethodTest methodTest2 = (MethodTest) constructor.newInstance("will",99);
    System.out.println(methodTest2.toString());
} catch (Exception e) {
    e.printStackTrace();
}

output:
MethodTest{name='will', age=99}

5. Understand the essence of generics

Let's look at an example first:

public class FanXingTest {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        ArrayList<String> list1 = new ArrayList<String>();
        list1.add("hello");
        //list1.add(20);编译错误
        Class c1 = list.getClass();
        Class c2 = list1.getClass();
        System.out.println(c1 == c2);
        //反射的操作都是编译之后的操作

        try {
            Method m = c2.getMethod("add",Object.class);
            m.invoke(list1,20);//绕过编译操作就绕过了泛型
            System.out.println(list1.size());
            System.out.println(list1);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
output:
true
2
[hello, 20]

By returning the result, we can see that the generic type of the collection is de-genericized after compilation. The generic type of the collection type in java is to prevent wrong input. It is only valid in the compilation stage, and bypassing the compilation is invalid, so We operate through method reflection, which can bypass compilation.

6. Dynamic proxy for reflective applications

动态代理It refers to the method that the client calls other objects through the proxy class, and the proxy object of the target class is dynamically created according to the need when the program is running.
Example code:

//动态代理的使用
interface Subject {
    void action();
}

//被代理类
class RealSubject implements Subject {

    @Override
    public void action() {
        System.out.println("我是被代理类,记得要执行我奥,么么~~~");
    }
}

class MyInvocationHandler implements InvocationHandler {

    Object object;//实现了接口的被代理类的对象的声明

    //①给被代理的对象实例化 ②返回一个代理类的对象
    public Object blind(Object object) {
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }

    //当通过代理类的对象发起对被重写的方法的调用时,都会转化为对如下的invoke方法的调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //method方法的返回值是returnVal
        Object returnVal = method.invoke(object,args);
        return returnVal;
    }
}


public class ProxyTest {
    public static void main(String[] args){
        //1. 创建被代理类对象
        RealSubject realSubject = new RealSubject();
        //2. 创建一个实现了InvocationHandler接口的类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        //3. 调用blind()方法,冬天的返回一个同样实现了real所在类实现的接口Subject的代理类的对象。
        Object object = handler.blind(realSubject);
        Subject subject = (Subject) object;//此时sub就是代理类的对象

        subject.action();//转到对InvacationHandler接口的实现类的invoke()方法的调用
    }
}
output:
我是被代理类,记得要执行我奥,么么~~~

Explain, understand the difference between dynamic proxy and static proxy. The so-called 静态代理proxy class and the class of the target object are determined during compilation, which is not conducive to the expansion of the program. That is, each proxy class can only serve one interface, which means that many proxy classes will be generated during program development.

7. Dynamic proxy and AOP


ctProxy
Converted from : to:
xyProxy

Example:

interface Human {
    void info();

    void fly();
}

class SuperMan implements Human {

    @Override
    public void info() {
        System.out.println("我是超人!");
    }

    @Override
    public void fly() {
        System.out.println("I believe I can fly!");
    }
}

class HumanUtil {
    public void method1() {
        System.out.println("=============方法一============");
    }

    public void method2() {
        System.out.println("=============方法二============");
    }
}

class MyInvocationHandler implements InvocationHandler {

    Object object;//被代理类对象的声明

    public void setObject(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        HumanUtil h = new HumanUtil();

        h.method1();

        Object returnVal = method.invoke(object, args);

        h.method2();

        return returnVal;
    }
}

//动态的创建一个代理类的对象
class MyProxy {
    public static Object getProxyInstance(Object object) {
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.setObject(object);

        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), handler);
    }
}

public class TestAOP {
    public static void main(String[] args){
        SuperMan man = new SuperMan();//创建一个被代理类的对象
        Human human = (Human) MyProxy.getProxyInstance(man);//返回一个代理类的对象
        human.info();//通过代理类的对象嗲用重写的抽象的方法
        System.out.println();
        human.fly();
    }

}

output:
=============方法一============
我是超人!
=============方法二============

=============方法一============
I believe I can fly!
=============方法二============

AOP proxy method:
AOPMethod

Related concepts

What is compilation?
Answer: Translating the original program into computer language is binary code. In java, it is the byte code that translates the .java file, that is, the source program into .class.
What is compile time?
Answer: In the process of translating the original program into computer language, the process of translating .java into .class files
What is runtime?
Answer: When starting the program, in java, the class loader loads the .class file and hands it to the jvm for processing
. What is a compiled language?
Answer: Convert the original program into binary code at one time, and then execute the program
. What is an interpreted language?
Answer: Convert a sentence, execute a sentence, java is a language that is both compiled and interpreted. The difference between a
compiled
language and an interpreted language: A: Compiled languages ​​are efficient and depend on the compiler, but they are poor in cross-platform and interpreted in low efficiency. Interpreter-dependent, but cross-platform strong
What is a class loader?
Answer: The class loader is the class loader in the JVM. Its function is to transport the compiled .class bytecode to the checker for security check. After the check is passed, it starts to interpret and execute
. What is runtime dynamic loading class?
Answer: Reflection is a mechanism that allows a program (class) to obtain information about the program (class) when it is running, that is, to obtain class information that is impossible to obtain at compile time, because this information is stored in the Class object. , and this Class object is dynamically loaded when the program is running
It is to dynamically load classes when the program is running, view class information, generate objects, or manipulate generated objects. When a class is running, you can get the information of the class, and you can dynamically modify the information, you can see yourself, just like looking in a mirror, the class object is generated at runtime, and the information of the class is manipulated through the class object. At runtime, when the program is run, the class loader will load the classes that are really needed. What is really needed? It is the class that really works, such as: there are object instances of this class, or the class calls static method attributes, etc.

Guess you like

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