1. What is java reflection?
In the object-oriented programming process of java, we usually need to know a Class class first, and then new 类名()
method to obtain the object of this class. That is to say, we need to know which class we want to instantiate and which method to run when we write the code (compile time or before compile time), which is usually called static class loading .
But in some scenarios, we don't know the specific behavior of our code in advance. For example, we define a service task workflow, and each service task is a method of a corresponding class.
- Which method of which class is executed by service task B is determined by the execution result of service task A
- Which method of which class is executed by service task C is determined by the execution results of service tasks A and B
- And the user does not want the function of the service task to be hard-coded in the code, and hopes to execute different programs through configuration
Faced with this situation, we can't use the code new 类名()
to achieve it, because you don't know how the user needs to do the configuration. This second he wants the service task A to execute the x method of the Xxxx class, and the next second He may wish to execute the y method of the Yyyy class. Of course, you can also say that you need to ask for it. The user changes the demand once, and I change the code once. This method can also be demanded, but it is a pain for both the user and the programmer, so is there a way to dynamically change the calling behavior of the program at runtime ? This is the " java reflection mechanism " that I want to introduce to you.
So what can the reflection mechanism of java do? Probably something like this:
- Dynamically instantiate class objects during program runtime
package名.类名
- Dynamically obtain information about class objects during program runtime, including cost variables and methods of objects
- Dynamically use the object's member variable properties at runtime
- Dynamically call the object's methods during program runtime (private methods can also be called)
2. Hello World
We define a class called Student
package com.zimug.java.reflection;
public class Student {
public String nickName;
private Integer age;
public void dinner(){
System.out.println("吃晚餐!");
}
private void sleep(int minutes){
System.out.println("睡" + minutes + "分钟");
}
}
If you don't use reflection, I believe that friends who have learned java will definitely call the dinner method
Student student = new Student();
student.dinner();
If it is reflection, how should we call it?
//获取Student类信息
Class cls = Class.forName("com.zimug.java.reflection.Student");
//对象实例化
Object obj = cls.getDeclaredConstructor().newInstance();
//根据方法名获取并执行方法
Method dinnerMethod = cls.getDeclaredMethod("dinner");
dinnerMethod.invoke(obj); //打印:吃晚餐!
Through the above code, we can see that the com.zimug.java.reflection.Student class name and the dinner method name are strings. Since it is a string, we can execute this program through a configuration file, or a database, or some other flexible configuration method. This is the most basic way to use reflection.
Third, the relationship between class loading and reflection
The class loading mechanism of java is quite complicated. In order not to confuse the key points, we will only introduce a part of the content related to "reflection".
When java executes compilation, the java file is compiled into a bytecode class file. The class loader loads the class file into the memory during the class loading phase and instantiates an object of java.lang.Class. For example: for the Student class in the loading phase
- Instantiate a Class object in memory (method area or code area), note that the Class object is not a Student object
- A Class class (bytecode file) corresponds to a Class object
- The Class object saves the basic information of the Student class, such as how many fields (Field) does this Student class have? How many constructors are there? How many methods are there? What annotations are there? and other information.
With the above basic information object (java.lang.Class object) about the Student class, the object of the Student class can be instantiated according to this information at runtime.
- You can directly new a Student object at runtime
- You can also use reflection to construct a Student object
But no matter how many Student objects you create, no matter how many Student objects you construct by reflection, there is only one java.lang.Class object that holds the Student class information. The code below can prove it.
Class cls = Class.forName("com.zimug.java.reflection.Student");
Class cls2 = new Student().getClass();
System.out.println(cls == cls2); //比较Class对象的地址,输出结果是true
Fourth, the java class that operates reflection
Knowing the above basic information, we can learn more about the classes and methods related to the reflection class:
- java.lang.Class: represents a class
- java.lang.reflect.Constructor: represents the constructor of the class
- java.lang.reflect.Method: represents the normal method of the class
- java.lang.reflect.Field: Represents a member variable of a class
- Java.lang.reflect.Modifier: Modifiers, modifiers for methods, modifiers for member variables.
- java.lang.annotation.Annotation: Annotations can be added to classes, member variables, constructors, and ordinary methods
4.1. Three ways to obtain Class objects
Class.forName()
method to get the Class object
/**
* Class.forName方法获取Class对象,这也是反射中最常用的获取对象的方法,因为字符串传参增强了配置实现的灵活性
*/
Class cls = Class.forName("com.zimug.java.reflection.Student");
类名.class
Get the Class object
/**
* `类名.class`的方式获取Class对象
*/
Class clz = User.class;
类对象.getClass()
method to obtain the Class object
/**
* `类对象.getClass()`方式获取Class对象
*/
User user = new User();
Class clazz = user.getClass();
While there are three ways to get the Class object of a class, only the first one can be called "reflection".
4.2. Obtain the basic information of the Class object
Class cls = Class.forName("com.zimug.java.reflection.Student");
//获取类的包名+类名
System.out.println(cls.getName()); //com.zimug.java.reflection.Student
//获取类的父类
Class cls = Class.forName("com.zimug.java.reflection.Student");
//这个类型是不是一个注解?
System.out.println(cls.isAnnotation()); //false
//这个类型是不是一个枚举?
System.out.println(cls.isEnum()); //false
//这个类型是不是基础数据类型?
System.out.println(cls.isPrimitive()); //false
The Class class object information includes almost all the information you want to know about this type definition, and more methods are not listed one by one. You can also use the following method
- Get what interfaces are implemented by the class represented by the Class class object: getInterfaces()
- Get which annotations are used by the class represented by the Class class object: getAnnotations()
4.3. Obtaining the member variables of the Class object
Combine the definition of the Student class above to understand the following code
Class cls = Class.forName("com.zimug.java.reflection.Student");
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field.getName()); //nickName
}
fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName()); //nickName 换行 age
}
- The getFields() method gets the non-private member variables of the class, an array, including member variables inherited from the parent class
- The getDeclaredFields method gets all member variables, arrays, but does not contain member variables inherited from the parent class
4.4. The method of obtaining the Class object
- getMethods() : Get all non-private methods of the class represented by the Class object, an array, including methods inherited from the parent class
- getDeclaredMethods() : Gets all methods, arrays defined by the class represented by the Class object, but does not contain methods inherited from the parent class
- getMethod(methodName): Get the non-private method of the specified method name of the class represented by the Class object
- getDeclaredMethod(methodName): The method to get the specified method name of the class represented by the Class object
Class cls = Class.forName("com.zimug.java.reflection.Student");
Method[] methods = cls.getMethods();
System.out.println("Student对象的非私有方法");
for (Method m : methods) {
System.out.print(m.getName() + ",");
}
System.out.println(" end");
Method[] allMethods = cls.getDeclaredMethods();
System.out.println("Student对象的所有方法");
for (Method m : allMethods) {
System.out.print(m.getName() + ",");
}
System.out.println(" end");
Method dinnerMethod = cls.getMethod("dinner");
System.out.println("dinner方法的参数个数" + dinnerMethod.getParameterCount());
Method sleepMethod = cls.getDeclaredMethod("sleep",int.class);
System.out.println("sleep方法的参数个数" + sleepMethod.getParameterCount());
System.out.println("sleep方法的参数对象数组" + Arrays.toString(sleepMethod.getParameters()));
System.out.println("sleep方法的参数返回值类型" + sleepMethod.getReturnType());
The execution result of the above code is as follows:
Student对象的非私有方法
dinner,wait,wait,wait,equals,toString,hashCode,getClass,notify,notifyAll, end
Student对象的所有方法
dinner,sleep, end
dinner方法的参数个数0
sleep方法的参数个数1
sleep方法的参数对象数组[int arg0]
sleep方法的参数返回值类型void
You can see that the methods obtained by getMethods include the methods defined in the Object parent class, but do not include the private method sleep defined in this class. In addition, we can also get the parameters and return value information of the method:
Get parameter-related properties:
- Get the number of method parameters: getParameterCount()
- Get the method parameter array object: getParameters(), the return value is the java.lang.reflect.Parameter array
Get the properties related to the return value
- Get the data type of the return value of the method: getReturnType()
4.5. Method call
In fact, the method call has been demonstrated above, as follows invoke calls the dinner method
Method dinnerMethod = cls.getDeclaredMethod("dinner");
dinnerMethod.invoke(obj); //打印:吃晚餐!
The dinner method has no parameters, so how to call the method with parameters? Look at the definition of the invoke method, the first parameter is the Method object, no matter how many parameters there are in the back Object... args
just pass the parameters in sequence according to the method definition. .
public Object invoke(Object obj, Object... args)
4.6. Creating an object of a class (instantiating an object)
//获取Student类信息
Class cls = Class.forName("com.zimug.java.reflection.Student");
//对象实例化
Student student = (Student)cls.getDeclaredConstructor().newInstance();
//下面的这种方法是已经Deprecated了,不建议使用。但是在比较旧的JDK版本中仍然是唯一的方式。
//Student student = (Student)cls.newInstance();
5. Common scenes of reflection
- Call a method of a class with configuration information
- Combine annotations to achieve special functions
- Load jar packages or classes on demand
5.1. Calling methods of a class through configuration information
Encapsulate the code in the hello world above. Do you know the class name className and method name methodName to call the method? As for whether you configure className and methodName to file, nacos or database, decide for yourself!
public void invokeClassMethod(String className,String methodName) throws ClassNotFoundException,
NoSuchMethodException,
InvocationTargetException,
InstantiationException,
IllegalAccessException {
//获取类信息
Class cls = Class.forName(className);
//对象实例化
Object obj = cls.getDeclaredConstructor().newInstance();
//根据方法名获取并执行方法
Method dinnerMethod = cls.getDeclaredMethod(methodName);
dinnerMethod.invoke(obj);
}
5.2. Combine annotations to achieve special functions
If you have studied mybatis plus, you should have learned such an annotation TableName, which indicates which table in the database corresponding to the current instance class Student. As shown in the code below, the class shown in Student corresponds to the table t_student.
@TableName("t_student")
public class Student {
public String nickName;
private Integer age;
}
Below we customize the annotation TableName
@Target(ElementType.TYPE) //表示TableName可作用于类、接口或enum Class, 或interface
@Retention(RetentionPolicy.RUNTIME) //表示运行时由JVM加载
public @interface TableName {
String value() ; //则使用@TableName注解的时候: @TableName(”t_student”);
}
With this annotation, we can scan java files under a certain path. As for the scanning of class annotations, we don't need to develop it ourselves, just introduce the following maven coordinates.
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.10</version>
</dependency>
Look at the following code: first scan the package, get the class marked with the TableName annotation from the package, and then print the annotation value information for the class
// 要扫描的包
String packageName = "com.zimug.java.reflection";
Reflections f = new Reflections(packageName);
// 获取扫描到的标记注解的集合
Set<Class<?>> set = f.getTypesAnnotatedWith(TableName.class);
for (Class<?> c : set) {
// 循环获取标记的注解
TableName annotation = c.getAnnotation(TableName.class);
// 打印注解中的内容
System.out.println(c.getName() + "类,TableName注解value=" + annotation.value());
The output is:
com.zimug.java.reflection.Student类,TableName注解value=t_student
Some friends will ask what is the use of this? This is of great use. With the corresponding relationship between the class definition and the database table, you can also obtain the member variables of the class through reflection. After that, can you construct the SQL for adding, deleting, modifying and checking according to the t_student and the field name nickName and age? Everything is built, is it just a basic Mybatis plus?
The combination of reflection and annotation can evolve many application scenarios, especially in terms of architecture optimization, waiting for you to discover!
5.3. Load jar package or class on demand
In some scenarios, we may not want the JVM loader to load all the jar packages into the JVM virtual machine at one time, because this will affect the startup and initialization efficiency of the project and take up more memory. We want to load on demand, which jars need to be used, and load these jars according to the requirements of the dynamic operation of the program.
//按路径加载jar包
File file = new File("D:/com/zimug/commons-lang3.jar");
URL url = file.toURI().toURL();
//创建类加载器
ClassLoader classLoader = new URLClassLoader(new URL[]{url});
Class cls = classLoader.loadClass("org.apache.commons.lang3.StringUtils");
Similarly, put the .class file in a path, we can also load it dynamically
//java的.class文件所在路径
File file = new File("D:/com/zimug");
URL url = file.toURI().toURL();
//创建类加载器
ClassLoader classLoader = new URLClassLoader(new URL[]{url});
//加载指定类,package全路径
Class<?> cls = classLoader.loadClass("com.zimug.java.reflection.Student");
Does the dynamic loading of classes remind you of anything? Is it possible to implement code modifications without restarting the container? Yes, this is the principle, because there is only one Class object for a class, so no matter how many times you reload it, the last loaded class object is used (mentioned above).
The advantages and disadvantages of reflection
- Advantages: Free, flexible use, not restricted by class access rights. The method call can be implemented according to the specified class name and method name, which is very suitable for flexible business configuration.
shortcoming:
- It is also because reflection is not restricted by class access rights, its security is low, and most of the java security problems are caused by reflection.
- Compared with normal object access calls, reflection has relatively low performance because of the instantiation process of classes and methods.
- Destroy the encapsulation of java classes, the information hiding and boundaries of classes are destroyed, please pay attention to my announcement number: Antetokounmpo, reply 003 and give the author's column "The Way of Docker Cultivation" PDF version, more than 30 fine docker articles. Antetokounmpo Blog: zimug.com
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。