Chapter 15 reflection mechanism
You need to master the specified structure of obtaining Class instances, creating runtime Class objects, and calling runtime classes.
Reflection overview
Reflection is the key to being regarded as a dynamic language. Reflection mechanism allows programs to obtain the internal information of any class with the help of Reflection API during execution, and can directly operate the internal properties and methods of any object.
After loading the Class, an object of Class type is generated in the method area of heap memory (a Class has only one Class object), which contains the complete structure information of the Class. We can see the structure of the Class through this object. This object is like a mirror, through which we can see the structure of the Class. Therefore, we call it reflection.
Supplement: dynamic language vs static language
1. Dynamic language
It is a kind of language that can change its structure at run time: for example, new functions, objects and even code can be introduced, existing functions can be deleted or other structural changes. Generally speaking, the code can change its structure according to some conditions at run time.
Main dynamic languages: Object-C, c#, JavaScript, PHP, Python, Erlang.
2. Static language
Corresponding to dynamic language, the language with immutable runtime structure is static language. Such as Java, C, C + +.
Java is not a dynamic language, but Java can be called a "quasi dynamic language". That is, Java has certain dynamics. We can use reflection mechanism and bytecode operation to obtain characteristics similar to dynamic language.
The dynamic nature of Java makes programming more flexible!
Main API s related to reflection
java.lang.Class: represents a class
java.lang.reflect.Method: represents the method of the class
java.lang.reflect.Field: represents the member variable of the class
java.lang.reflect.Constructor: represents the constructor of the class
... ...
Reflexive acquaintance
public class Person { private String name; public int age; public Person() { } private Person(String name) { this.name = name; } public Person(String name, int age) { this.name = name; this.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; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public void show(){ System.out.println("I am a person"); } private String showNation(String nation){ System.out.println("My nationality is" + nation); return nation; } } public class ReflectionTest { //Operation on Person before reflection @Test public void test1(){ //1. Create the object of Person class Person p1 = new Person("Tom",12); //2. Call its internal properties and methods through the object p1.age = 10; System.out.println(p1); p1.show(); //Outside the Person class, its internal private structure cannot be called through the object of the Person class //For example: name, showNation() and private constructor } //After reflection, the operation of Person @Test public void test2() throws Exception{ Class clazz = Person.class; //1. Create the object of Person class through reflection Constructor cons = clazz.getConstructor(String.class, int.class); Object obj = cons.newInstance("Tom",12); Person p = (Person) obj; System.out.println(p); //2. Call the attributes and methods specified by the object through reflection //Call properties Field age = clazz.getDeclaredField("age"); age.set(p,10); System.out.println(p); //Call method Method show = clazz.getDeclaredMethod("show"); show.invoke(p); //Through reflection, you can call the private structure of the Person class. For example: private constructors, methods and properties //Call private constructor Constructor cons1 = clazz.getDeclaredConstructor(String.class); cons1.setAccessible(true); Person p1 = (Person) cons1.newInstance("Jerry"); System.out.println(p1); //Call private properties Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1,"Lilei"); System.out.println(p1); //Call private methods Method showNation = clazz.getDeclaredMethod("showNation", String.class); showNation.setAccessible(true); String nation = (String) showNation.invoke(p1, "China");//Equivalent to string nation = P1 Shownation ("China"); System.out.println(nation); } }
Reflection can not only call public constructors, properties and methods; You can also call private constructors, properties, and methods.
This will involve some questions:
Question 1: public structures can be called through new or reflection. Which one is used in development?
Suggestion: new directly.
When will reflection be used: the characteristics of reflection: dynamics. When you want to modify objects, create new objects, properties, methods, etc. during execution, you can use reflection.
Question 2: does reflection mechanism contradict encapsulation in object-oriented? How to treat the two technologies?
No contradiction.
Encapsulation reflects the private structure, which does not mean that you are not allowed to call. It is that the private structure has been encapsulated in the public method or constructor. It is OK to call the public directly. There is no need to call the private structure. Therefore, encapsulation reflects how to do it. It is recommended not to call private structures.
Reflection is a question of whether you can call private constructors, properties and methods. However, encapsulation suggests that you do not call private ones. However, if you want to call, you can call them through the reflection mechanism.
About Java Understanding of lang.class class
1. Class loading process:
The program passes javac After the EXE command, one or more bytecode files (. End of class) will be generated.
Then we use Java Exe command to interpret and run a bytecode file. It is equivalent to loading a bytecode file into memory. This process is called Class loading (excluding compilation). The Class loaded into memory is called the runtime Class. This runtime Class is used as an instance of Class.
2. In other words, the instance of class corresponds to a runtime class loaded into memory. (this also shows that the class instance is not new.). Class instance is a class.
3. The runtime class loaded into memory will be cached for a certain time. Within this time, we can get this runtime class in different ways.
At this point, it is added that everything is an object. Object xxx, File, URL, reflection, front end, database operation
How to get an instance of Class
How to obtain the instance of Class (the first three methods need to be mastered). Among them, mode 3 is the one with the highest frequency.
The runtime classes loaded into memory will be cached for a certain period of time. Within this time, we can get this runtime class in different ways. The runtime class is the same throughout its life cycle. It's just obtained in different ways.
The reason why mode 1 is rarely used is that reflection is used because of the dynamic nature of reflection. However, the mode is always written dead and cannot be modified. Only a Class instance can be obtained, which does not reflect the dynamic nature. Mode 3 reflects the dynamic nature. Only when it is actually running can it be determined which Class instance to get.
/* The first three ways to get instances of Class */ @Test public void test3() throws ClassNotFoundException { //Method 1: call the properties of the runtime class: class Class<Person> clazz1 = Person.class; System.out.println(clazz1); //Method 2: call getClass() through the object of the runtime class Person p1 = new Person(); Class<? extends Person> clazz2 = p1.getClass(); System.out.println(clazz2); //Among them, mode 3 is the one with the highest frequency. //Method 3: call the static method of Class: forName(String classPath) //You must specify under which package, because different packages may have the same class name Class<?> clazz3 = Class.forName("com.exer.later.Person"); // clazz3 = Class.forName("java.lang.String"); System.out.println(clazz3); /* The runtime classes loaded into memory will be cached for a certain period of time. Within this time, we can get this runtime class in different ways. The runtime class is the same throughout its life cycle. It's just obtained in different ways. */ System.out.println(clazz1 == clazz2);//true System.out.println(clazz1 == clazz3);//true //Method 4: use class loader: ClassLoader ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class<?> clazz4 = classLoader.loadClass("com.exer.later.Person"); System.out.println(clazz4); System.out.println(clazz1 == clazz4);//true }
What types can have Class objects?
1.class: external class, member (member internal class, static internal class), local internal class, anonymous internal class
2.interface: Interface
3. []: array. Note that as long as the element type of the array is the same as the dimension, it is the same Class
4.enum: Enumeration
5.annotation: annotation @ interface
6.primitive type: basic data type
7.void
//Description of which structures Class instances can be: Class c1 = Object.class; Class c2 = Comparable.class; Class c3 = String[].class; Class c4 = int[][].class; Class c5 = ElementType.class; Class c6 = Override.class;//annotation Class c7 = int.class; Class c8 = void.class; Class c9 = Class.class; int[] a = new int[10]; int[] b = new int[100]; Class c10 = a.getClass(); Class c11 = b.getClass(); // As long as the element type is the same as the dimension, it is the same Class System.out.println(c10 == c11);//true
Class loading process
- Load: load the bytecode content of the class file into memory, convert these static data into the runtime data structure of the method area, and then generate a Java. XML file representing this class Lang. class object, as the access entry (i.e. reference address) of class data in the method area. All class data that needs to be accessed and used can only be accessed through this class object. This loading process requires the participation of the class loader.
- Link: the process of merging the binary code of Java classes into the running state of the JVM.
- Verification: ensure that the loaded class information complies with the JVM specification. For example, starting with cafe, there is no security problem
- Preparation: the stage of formally allocating memory for class variables (static) and setting the default initial value of class variables. These memory will be allocated in the method area.
- Resolution: the process of replacing the symbolic reference (constant name) in the virtual machine constant pool with a direct reference (address).
- initialization:
- The process of executing a class constructor () method. The class constructor () method is generated by the combination of the assignment actions of all class variables in the class automatically collected at compile time and the statements in the static code block. (the class constructor is the constructor that constructs class information, not the constructor that constructs the class object.).
- When initializing a class, if it is found that its parent class has not been initialized, it needs to be triggered first
- Virtual chance ensures that the () methods of a class are locked and synchronized correctly in a multithreaded environment.
give an example:
public class ClassLoadingTest { public static void main(String[] args) { System.out.println(A.m); } } class A { static { m = 300; } static int m = 100; } //Step 2: m=0 after link //Step 3: after initialization, the value of m is determined by the < clinit > () method // The class constructor < clinit > () method of A is generated by combining the assignment of class variables and the statements in the static code block in order, similar to // <clinit>(){ // m = 300; // m = 100; // }
Understanding of ClassLoader
Function of class loader:
- The function of class loading: load the bytecode content of the class file into memory, convert these static data into the runtime data structure of the method area, and then generate a Java. XML file representing this class in the heap Lang. class object, as the access entry of class data in the method area.
- Class caching: the standard Java se class loader can find classes as required, but once a class is loaded into the class loader, it will remain loaded (cached) for a period of time. However, the JVM garbage collection mechanism can recycle these class objects.
Class loader is used to load classes into memory. The JVM specification defines loaders for classes of the following types.
public class ReflectionTest{ /* Understand class loaders */ @Test public void test4(){ //For custom classes, use the system class loader to load them ClassLoader classLoader = ReflectionTest.class.getClassLoader();//Gets the class loader of the current class System.out.println(classLoader); //Call getParent() of system class loader: get extended class loader ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1); //Calling getParent() of extended class loader: unable to get boot class loader //The boot class loader is mainly responsible for loading the core class library of java, and cannot load the custom class. ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2);//null cannot get //Core class library String ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);//null cannot get } }
Use ClassLoader to load configuration files (need to master)
Note that the default read paths of the two are different. All need to master.
Using IO stream to read is under the current Module by default, and using ClassLoader is under the src of the current Module by default.
public class ReflectionTest{ @Test public void test5() throws IOException { Properties pros = new Properties(); //At this time, the file is under the current module by default. //How to read the configuration file // FileInputStream fis = new FileInputStream("jdbc.properties"); // pros.load(fis); //The second way to read the configuration file is to use ClassLoader //The default identification of the configuration file is: under src of the current module //Note: in the web phase, the configuration file is stored under src. If you use method 1, you need to fill in src \ \ JDBC properties ClassLoader classLoader = ReflectionTest.class.getClassLoader();//Gets the class loader of the current class InputStream is = classLoader.getResourceAsStream("jdbc.properties"); pros.load(is); String user = pros.getProperty("user"); String password = pros.getProperty("password"); System.out.println("user = " + user + ",password = " + password); } }
An object that creates a runtime class through reflection
The only way to create an object is to call the constructor. Even if an object is created by calling other methods, it is because the constructor is encapsulated and called inside this method.
newInstance(): call this method to create the object of the corresponding runtime class. The null parameter constructor of the runtime class was called internally.
To create the object of the runtime class normally with this method, you need to:
1. The runtime class must provide a constructor with null parameters
2. The constructor of null parameter has enough access rights. It is usually set to public.
A public null parameter constructor is required in JavaBean. reason:
1. It is convenient to create objects of runtime classes through reflection
2. When a subclass inherits this runtime class, it is guaranteed that the parent class has this constructor when super() is called by default
public class NewInstanceTest { //Create an object of the corresponding runtime class through reflection @Test public void test() throws Exception { Class<Person> clazz = Person.class;//Gets the Class instance of the Person Class /* The only way to create an object is to call the constructor. Even if an object is created by calling other methods, it is because the constructor is encapsulated and called inside this method newInstance():Call this method to create the object of the corresponding runtime class. The null parameter constructor of the runtime class was called internally. To create the object of the runtime class normally with this method, you need to: 1.The runtime class must provide a constructor with null parameters 2.Empty argument constructor has sufficient access rights. It is usually set to public. The public bean constructor requires an empty parameter. reason: 1.It is convenient to create objects of runtime classes through reflection 2.When a subclass inherits this runtime class, it is guaranteed that the parent class has this constructor when super() is called by default */ Person obj = clazz.newInstance(); System.out.println(obj); } }
Experience the dynamics of reflection
Experience the dynamic nature of reflection. It is impossible to predict which object will be created in the compilation stage. Only when it is running can it be known. Reflection is widely used in the back frame.
//Experience the dynamic nature of reflection. It is impossible to predict which object will be created in the compilation stage. Only when it is running can it be known //Reflection is widely used in the back frame @Test public void test1(){ int num = new Random().nextInt(3);//0,1,2 String classPath = ""; switch (num){ case 1: classPath = "java.util.Date"; break; case 2: classPath = "java.lang.Object"; break; case 0: classPath = "com.exer.later.Person"; break; } Object obj = null; try { obj = getInstance(classPath); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } System.out.println(obj); } public Object getInstance(String classPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class<?> clazz = Class.forName(classPath); Object obj = clazz.newInstance(); return obj; }
Gets the complete structure of the runtime class
Gets the attribute structure and its internal structure of the runtime class
It will not be done in actual development, but we need to know that it can be done. Pay attention to the difference between Declared and not
@Test public void test2(){ Class<Person> clazz = Person.class; //Get attribute structure //getFields(): get the attributes declared as public access rights in the current runtime class and its parent class Field[] fields = clazz.getFields(); for (Field f : fields) { System.out.println(f); } //Declared //Getdeclaraedfields(): get all attributes (all permissions) declared by the current runtime class. (excluding properties declared in the parent class) Field[] declaredFields = clazz.getDeclaredFields(); for (Field f : declaredFields) { System.out.println(f); } } //Permission modifier data type variable name @Test public void test3(){ Class<Person> clazz = Person.class; Field[] declaredFields = clazz.getDeclaredFields(); for (Field f : declaredFields) { //1. Permission modifier int modifier = f.getModifiers();//The returned is int, which needs to be translated System.out.println(Modifier.toString(modifier));//Call the toString method of the Modifier class //2. Data type Class<?> type = f.getType(); System.out.println(type.getName()); //3. Variable name String name = f.getName(); System.out.println(name); } }
Gets the method structure of the runtime class
Same as attribute.
@Test public void test4(){ Class<Person> clazz = Person.class; //Get all the permissions in the current run-time method and its parent class: public method() Method[] methods = clazz.getMethods(); for (Method m : methods) { System.out.println(m); } //Getdeclaraedmethods(): get all the methods declared in the current runtime class. (excluding methods declared in the parent class) Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method m : declaredMethods) { System.out.println(m); } }
Gets the internal structure of the runtime class method
Frame = annotation + reflection + design pattern
/* Reflection gets the internal structure of the method of the class @Xxx annotation Permission modifier return value type method name (parameter list 1, parameter name 1,...) throws XxxException{} */ @Test public void test5(){ Class<Person> clazz = Person.class; Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method m : declaredMethods) { //1. Get the annotation of the method declaration (the declaration cycle of the annotation is required to be sufficient and declared as RunTime) Annotation[] annos = m.getAnnotations();//There may be multiple annotations with repeatable annotations for (Annotation a: annos) { System.out.println(a); } //2. Permission modifier System.out.println(Modifier.toString(m.getModifiers())); //3. Return value type System.out.println(m.getReturnType().getName()); //4. Method name System.out.println(m.getName()); //5. Formal parameter list (there can be multiple) Class<?>[] parameterTypes = m.getParameterTypes(); if (!(parameterTypes == null && parameterTypes.length == 0)){ for (int i = 0; i < parameterTypes.length; i++) { if ( i == parameterTypes.length - 1){//the last one System.out.println(parameterTypes[i].getName() + "args_" + i); break; } System.out.println(parameterTypes[i].getName() + "args_" + i + ","); } } //6. Exceptions thrown (multiple exceptions are allowed) Class<?>[] exceptionTypes = m.getExceptionTypes(); if (exceptionTypes.length > 0){ System.out.print("throws"); for (int i = 0; i < exceptionTypes.length; i++) { if ( i == exceptionTypes.length - 1){//the last one System.out.println(exceptionTypes[i].getName()); break; } System.out.println(exceptionTypes[i].getName() + ","); } } } }
Gets the constructor of the class
Note: getConstructors(): get the constructor declared as public in the current runtime class. (this is different from methods and properties, and it is useless to obtain the constructor of the parent class)
//Get constructor structure @Test public void test6(){ Class<Person> clazz = Person.class; //getConstructors(): get the constructor declared as public in the current runtime class. (this is different from methods and properties, and it is useless to obtain the constructor of the parent class) Constructor<?>[] constructors = clazz.getConstructors(); for (Constructor c : constructors) { System.out.println(c); } //Getdeclaraedconstructors(): get all constructors declared in the current runtime class Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); for (Constructor c : declaredConstructors) { System.out.println(c); } }
Gets the generics of the parent class of the runtime class with generics
Code is divided into: logical code vs functional code. Some code can be encapsulated and reused.
@Test public void test7(){ Class<Person> clazz = Person.class; //Gets the parent class of the runtime class Class<? super Person> superclass = clazz.getSuperclass(); System.out.println(superclass); //Gets the parent class of the runtime class with generics Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass); //Gets the generics of the parent class of the runtime class with generics. In the application scenario, read the corresponding types from the database DAO layer ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; //Get generic type Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); //Get an array. Here we simply look at the one with index 0. For example, Map(K,V) has two generic types // System.out.println(actualTypeArguments[0].getTypeName()); System.out.println(((Class)actualTypeArguments[0]).getName()); }
Get the interface, package and annotation of the runtime class
@Test public void test8(){ Class<Person> clazz = Person.class; //Gets the interface implemented by the runtime class Class<?>[] interfaces = clazz.getInterfaces(); for (Class c : interfaces) { System.out.println(c); } //Gets the interface implemented by the parent class of the runtime class Class<?>[] interfaces1 = clazz.getSuperclass().getInterfaces(); for (Class c : interfaces1) { System.out.println(c); } //Gets the package where the runtime class is located Package pack = clazz.getPackage(); System.out.println(pack); //Gets the annotation of the runtime class declaration Annotation[] annotations = clazz.getAnnotations(); for (Annotation a : annotations) { System.out.println(a); } }
The generic type in the parent class will be used later, the interface implemented by the runtime class will be used later, and the annotation will be used later.
Call the specified structure of the runtime class (Master)
The specified property in the action runtime class
Only the second method is enough.
First create the object of the runtime class.
1. Getdeclaraedfield (string fieldname): get the attribute of the specified variable name in the runtime class
2.setAccessible(true);
3. Get and set the property value of the specified object
@Test public void test9() throws Exception { Class<Person> clazz = Person.class; //Create an object of a runtime class Person person = clazz.newInstance(); //Get the specified property: the property in the runtime class is required to be declared public //This method is usually not used because there are few properties that are really declared as public Field id = clazz.getField("id"); /* Sets the value of the current property set():Parameter 1: indicates which object's attribute is set. Parameter 2: how much is this attribute value set */ id.set(person,1001); /* Gets the value of the current property get():Parameter 1: get the current attribute value of which object */ int pId = (int) id.get(person);//The default value is Object, and forced conversion is performed System.out.println(pId); } //Use more or the following methods @Test public void test10() throws Exception { Class<Person> clazz = Person.class; //Create an object of a runtime class Person person = clazz.newInstance(); //1. Getdeclaraedfield (string fieldname): get the attribute of the specified variable name in the runtime class Field name = clazz.getDeclaredField("name"); //2. Ensure that the current attribute is accessible name.setAccessible(true); //3. Get and set the property value of the specified object name.set(person,"Tom"); }
Call the method specified in the runtime class
First create the object of the runtime class.
1. Get a specified method
2.setAccessible(true);
3. invoke() of calling method: parameter 1: caller of method; parameter 2: actual parameter assigned to method parameter
Call static methods. (similarly, you can write out static attributes)
@Test public void test11() throws Exception{ Class<Person> clazz = Person.class; //Create an object of a runtime class Person person = clazz.newInstance(); /* 1.Gets a specified method getDeclaredMethod():Parameter 1: indicates the name of the obtained method parameter 2: indicates the formal parameter list of the obtained method Because there is a method overload, you need to specify the formal parameter list of the method */ Method show = clazz.getDeclaredMethod("show", String.class); //2. Ensure that the current method is accessible show.setAccessible(true); /* 3.invoke() of the calling method: parameter 1: caller of the method parameter 2: argument that assigns value to the method parameter invoke()The return value is the return value of the method called in the corresponding class. */ Object returnValue = show.invoke(person, "CHN"); System.out.println(returnValue); //How to call static methods. (similarly, you can write out static attributes) //private static void showDesc() Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); //invoke() returns null if the method in the called runtime class does not return a value // Object returnVal = showDesc.invoke(null);// Only static methods can be used in this way, but they are generally not used in this way Object returnVal = showDesc.invoke(Person.class);//The static method is called with the current class, but showDesc must be called by an instance of clazz, which is the class instance of this class, so it can only be this class, that is, even if it is not written, it knows which call it is, so it does not really execute person Class (also explains why null is OK) System.out.println(returnVal); }
Call the constructor specified by the runtime class (don't master)
Because calling constructors is to create objects, and we mostly use newInstance() to create objects, so this method is for understanding. Just know that you can do this.
Get the Class instance first
1. Get the specified constructor.
2.setAccessible(true);
3. Call this constructor to create the object of the runtime class
//How to call the constructor specified by the runtime class (rarely used). Because calling constructors is to create objects, and we mostly use newInstance() to create objects @Test public void test12() throws Exception { Class<Person> clazz = Person.class; //private Person(String name) /* 1.Gets the specified constructor. (there is no need to specify the constructor name, because the constructor name is the same as the class name) getDeclaredConstructor():Parameter: indicates the parameter list of the constructor */ Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class); //2. Make sure this constructor is accessible declaredConstructor.setAccessible(true); //3. Call this constructor to create the object of the runtime class Person person = declaredConstructor.newInstance("Tom"); System.out.println(person); }
Summary: how to create class objects
Mode 1: new + constructor
Method 2: to create an object of class Xxx, you can consider checking whether there are static methods in classes Xxx, Xxxs, XxxFactory and XxxBuilder. You can call its static method to create an Xxx object.
Mode 3: through reflection.
Give priority to mode 1, followed by mode 2 and mode 3 as a last resort.
Application of reflection: dynamic proxy
The proxy mode must have an interface, which is implemented by both the proxy class and the proxy class.
Static proxy example
Features: the proxy class and the proxy class are determined during compilation
/* Static proxy example Features: the proxy class and the proxy class are determined during compilation */ interface ClothFactory{ void produceCloth(); } //proxy class class ProxyClothFactory implements ClothFactory{ private ClothFactory factory;//Instantiate with the proxied class object public ProxyClothFactory(ClothFactory factory) { this.factory = factory; } @Override public void produceCloth() { System.out.println("Agent factory to make preparations"); factory.produceCloth(); System.out.println("Act as an agent for the factory to finish the work"); } } //Proxy class class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("Nike The factory produces sportswear"); } } public class StaticProxyTest { public static void main(String[] args) { //Create the object of the proxied class NikeClothFactory nike = new NikeClothFactory(); //Create object of proxy class ProxyClothFactory factory = new ProxyClothFactory(nike); factory.produceCloth(); } }
Dynamic agent example
The code logic is very strong. It is a dynamic generation of an agent, which has not been determined.
/* Examples of dynamic agents */ interface Human{ String getBelief(); void eat(String food); } //Proxy class class SuperMan implements Human{ @Override public String getBelief() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("I love eating" + food); } } /* What problems need to be solved in order to realize dynamic agent? Question 1: how to dynamically create a proxy class and its object according to the proxy class loaded into memory Question 2: when method a is called through the object of the proxy class, how to dynamically call method a with the same name in the proxy class. */ class ProxyFactory{ //Call this method to return an object of a proxy class. Solve problem 1 public static Object getProxyInstance(Object obj){//obj object of the proxied class MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(),handler); } } class MyInvocationHandler implements InvocationHandler{ private Object obj;//You need to use the object of the proxy class for assignment public void bind(Object obj){ this.obj = obj; } //When we call method a through the object of proxy class, we will automatically call the following method: invoke() //The function of method a to be executed by the proxy class is declared in invoke() @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //Method: that is, the method called for the proxy class object, which is also the method to be called by the proxy class object //obj: object of the proxied class Object returnVal = method.invoke(obj, args); //The return value of the above method is the return value of invoke() in the current class. return returnVal; } } public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); //proxyInstance: object of proxy class Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);//The dynamically generated proxy class and the proxy class implement the same interface //When a method is called through a proxy class object, it will automatically call the method with the same name in the proxy class String belief = proxyInstance.getBelief(); System.out.println(belief); proxyInstance.eat("Spicy hot pot"); NikeClothFactory nikeClothFactory = new NikeClothFactory(); ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory); proxyClothFactory.produceCloth(); } }
Dynamic agent and AOP (understand)
AOP(Aspect Orient Programming).
It is difficult to see the advantages of this dynamic Proxy in the Proxy and InvocationHandler described above. Here is a more practical dynamic Proxy mechanism.
Before improvement:
After improvement:
Improved description: code segment 1, code segment 2, code segment 3 and dark code segment are separated, but code segments 1, 2 and 3 are coupled with A specific method A! The most ideal effect is that code blocks 1, 2 and 3 can execute method A without directly calling the method of dark code in the way of hard coding in the program.
Code example:
public interface Dog{ void info(); void run(); } public class HuntingDog implements Dog{ public void info(){ System.out.println("I'm a hound"); } public void run(){ System.out.println("I run fast"); } } public class DogUtil{ public void method1(){ System.out.println("=====General simulation method I====="); } public void method2(){ System.out.println("=====General simulation method II====="); } } public class MyInvocationHandler implements InvocationHandler{ // Object to be represented private Object target; public void setTarget(Object target){ this.target = target; } // When all methods of the dynamic proxy object are executed, they will be replaced by the following invoke method public Object invoke(Object proxy, Method method, Object[] args) throws Exception{ DogUtil du = new DogUtil(); // Execute method1 in the DogUtil object. du.method1(); // Execute the method method with target as the main tone Object result = method.invoke(target , args); // Execute method2 in the DogUtil object. du.method2(); return result; } } public class MyProxyFactory{ // Generates a dynamic proxy object for the specified target public static Object getProxy(Object target) throws Exception{ // Create a myinvocationhandler object MyInvokationHandler handler = new MyInvokationHandler(); // Set the target object for myinvocationhandler handler.setTarget(target); // Create, and return a dynamic proxy object return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),handler); } } public class Test{ public static void main(String[] args) throws Exception{ // Create an original HuntingDog object as the target Dog target = new HuntingDog(); // Creates a dynamic proxy with the specified target Dog dog = (Dog)MyProxyFactory.getProxy(target); dog.info(); dog.run(); } }
- When using Proxy to generate a dynamic Proxy, it often does not generate a dynamic Proxy out of thin air, which is of little significance. Usually, dynamic Proxy is generated for the specified target object
- This dynamic agent is called AOP agent in AOP. AOP agent can replace the target object. AOP agent includes all methods of the target object. However, there are differences between the methods in AOP proxy and the methods of target object: the methods in AOP proxy can insert some general processing before and after the execution of target method
Thinking mode of learning
1. Focus on the big picture and start with the small
2. Reverse thinking and counter evidence method
For example, when talking about the quality of a thing, you can first think backwards about what would happen without it
3. See the essence through problems
A little intolerance makes a big plan
He who knows current affairs is a hero