reference resources: https://www.bilibili.com/video/BV1Kb411W75N
Principle of agent design pattern
Wrap the object with a proxy object and replace the original object with the proxy object. Any call to the original object must pass through the proxy. The proxy object determines whether and when to transfer the method call to the original object.
Static proxy
package com.a.java; /* Static proxy Features: the proxy class and the proxied 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 the factory to do some preparatory work"); factory.produceCloth(); System.out.println("Agent the factory to do some follow-up closing work"); } } // Proxy class class NikeClothFactory implements ClothFactory { @Override public void produceCloth() { System.out.println("Nike The factory produces a batch of 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 proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); } }
The above code first declares an interface ClothFactory, in which there is only one method productcloth with no return value. Then it declares a proxy class proxyclothfactory and a proxy class nikelothfactory, both of which implement the closetfactory interface. Among them, the producecloth method of the represented nikelothfactory only outputs one sentence: "Nike factory produces a batch of sportswear". The proxy class proxycloshfactory declares a private variable factory of closhfactory type and instantiates the factory through a parameterized construction method. This factory is instantiated with the proxied class object. After that, the produceCloth() of factory is called in the produceCloth() of the proxy class ProxyClothFactory, that is, the produceCloth() of the proxy class.
The main method first creates the object of the proxy class, then creates the proxy class object by the object of the proxy class as the parameter, and finally calls the produceCloth() of the proxy class. Because the proxy class's produceCloth() also calls the produceCloth() of the proxy class, the produceCloth() of the proxy class is also called.
The above code is a static proxy, and its proxy class and proxy class are written dead.
Dynamic agent
There are two problems to be solved in implementing dynamic agent:
- How to dynamically create a proxy class and its object according to the proxy class loaded into memory?
- 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?
The first question uses proxy The newproxyinstance method solves the second problem by implementing the invoke method in the InvocationHandler interface. Here is the introduction:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) Params: loader – Define the class loader of the proxy class (the same as the proxy class) interfaces – List of interfaces to be implemented by the proxy class (the same as the proxy class) h – The call handler to which the method call is dispatched Returns: Returns an object of a proxy class
The third parameter InvocationHandler above is an interface:
public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
It declares an invoke method with three parameters:
Proxy – the proxy instance that calls the method
Method – the method declared in the interface implemented by the proxy class
args – parameter of method
Return value: the return value after the method is executed
Therefore, in the code of dynamic proxy, we should write a class to implement InvocationHandler interface.
package com.a.java; /* Dynamic agent */ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; 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); } } /* Problems to be solved in implementing dynamic agent: 1. How to dynamically create a proxy class and its object according to the proxy class loaded into memory? 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 { // By calling this method, return an object of proxy class to 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 method a is called through the object of the proxy class, the following invoke() will be called automatically // 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: the method called by the proxy class object. This method is also used as the method to be called by the proxy class object // obj: object of the proxied class Object returnValue = method.invoke(obj, args); // The return value of the method called by the proxy class object, that is, the method to be called by the proxy class object, is used as the return value of invoke() in the current class return returnValue; } } public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); // proxyInstance: object of proxy class Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); // 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("Sichuan Mala"); System.out.println("******************************************"); NikeClothFactory nikeClothFactory = new NikeClothFactory(); ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory); proxyClothFactory.produceCloth(); } }
The above code first declares a Human interface, which is then implemented by the proxy class SuperMan and ProxyFactory class. The getProxyInstance method in ProxyFactory will dynamically return an object of the proxy class, which solves problem 1. The specific implementation is through proxy Newproxyinstance method, which is a method of a built-in class in the system, has three parameters. The first parameter is the same ClassLoader as the proxy class, the second parameter is the interface implemented by the proxy class, and the third parameter is the InvocationHandler type, which is an interface. Therefore, we also need to write a class to implement the InvocationHandler interface, which is the MyInvocationHandler class written later. The MyInvocationHandler class implements the invoke method. The second parameter method of the invoke method is the method called by the proxy class object. This method is also used as the method to be called by the proxy class object. The third parameter is the parameter of the method. With the method and parameter, there needs to be an object. To establish the relationship between the proxy class object and MyInvocationHandler class, Therefore, an obj attribute is declared and the object of the proxy class is assigned to obj with the bind method. Then execute the method method of obj in the invoke method and return the result. In this way, problem 2 is solved and the implementation of dynamic agent is completed.
In the main method, first instantiate the proxied class SuperMan, and then use proxyfactory Getproxyinstance (SuperMan) creates the object of the proxy class. Then, when the getBelief and eat methods are called through the proxy class object, the method with the same name in the proxy class will be called automatically. Later, the nikelothfactory class is instantiated, and then through proxyfactory Getproxyinstance (NikeClothFactory) creates an object of proxy class, and can execute the produceCloth method of nikelothfactory class again. This reflects the dynamic nature of dynamic agents.