The core of AOP is dynamic agent mode.
To master AOP, we must master the agent mode.
There are two agent modes: static agent and dynamic agent.
Before learning dynamic agents, master static agents.
1, Static proxy
There are four roles:
1. Abstract role
Interfaces or abstract classes are generally used to solve this problem. For example, renting a house.
Abstract role public interface Rent { void rent(); }
2. Real role
The role of the agent. For example, the landlord.
public class Host implements Rent { @Override public void rent() { System.out.println("I'm the landlord. I have a house"); } }
3. Agent role
Agent the real role, and add some additional operations. For example, mediation.
public class Proxy { private Rent rent; //Polymorphism! public Proxy(Rent rent) { this.rent = rent; } public void rent(){ seeHouse(); rent.rent(); fare(); } public void seeHouse(){ System.out.println("Look at the house"); } public void fare(){ System.out.println("Charge intermediary fee"); } }
4. Customer (test)
Who accesses the proxy role.
public class Client { public static void main(String[] args) { Host host = new Host(); Proxy proxy = new Proxy(host); proxy.rent(); } }
2, JDK native dynamic proxy
1. Understand why dynamic proxies are written like this
Our needs
Dynamic proxy is to create proxy class objects according to the proxy class loaded into memory randomly.
To meet this requirement, our template requirements for proxy classes are as follows:
How to solve
First, template can get the value of the method name, method parameter and method in the proxy class based on reflection.
Second, bind the proxy class and the proxy class
Third, the proxy object execution method is essentially the execution of the proxy object.
Suppose the proxy class object is A. The proxied class object is B.
A. The internal of method1 (arg1, arg2) is
A.method(){
B.method1(arg1,arg2);
}
Therefore, such A method handler is required in A
A.method1(arg1,arg2) gets the method name and method parameters
Then duplicate the method1 method in the method and operate it in a black box. Let B call this method.
This method handler is the InvocationHandler.
Since the B object is called internally, B should appear as an attribute in InvocationHandler and be assigned by constructor or method.
2. Case 1: Rental case
Four roles plus a method handler
1. Abstract role
public interface Rent { void rent(); }
2. Real role
landlord or landlady
public class Host implements Rent { @Override public void rent() { System.out.println("I'm the landlord. I have a house"); } }
3. Agent role
public class ProxyRent { public static Object getProxyInstance(Object obj){ MyInvocationHandler handler = new MyInvocationHandler(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } }
4. Method handling procedure
public class MyInvocationHandler implements InvocationHandler { private Object obj; public MyInvocationHandler(Object obj) { this.obj=obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { seeHouse(); Object returnValue = method.invoke(obj, args); fare(); return returnValue; } public void seeHouse(){ System.out.println("Look at the house"); } public void fare(){ System.out.println("Charge intermediary fee"); } }
5. Customer (test)
public class Client { public static void main(String[] args) { Host host = new Host(); Rent proxyInstance = (Rent) ProxyRent.getProxyInstance(host); //When cast, it can only be converted to the parent interface, not to the subclass that implements this interface proxyInstance.rent(); } }
be careful:
When cast, it can only be converted into a parent interface, not a subclass that implements this interface.
ProxyRent.getProxyInstance(host) returns the object of the proxy class that implements the Rent interface. Only objects of type Rent can be cast.
2. Case 2: Superman case
1. Abstract role
public interface Human { String getBelieve(); void eat(String food); }
2. Real role
public class SuperMan implements Human { @Override public String getBelieve() { return "I believe I can fly!"; } @Override public void eat(String food) { System.out.println("I love eating"+food); } }
3. Agent role
public class ProxyFactory { //The class of the proxy object returns a parameter of the proxy object public static Object getProxyInstance(Object obj){ MyInvocationHandler handler = new MyInvocationHandler(obj); //Proxy.newProxyInstance() this method is specifically used to create and return the object of the proxy class //Parameter 1: class loader of the runtime class of the proxied class object; Class loader is used to load bytecode files into memory and dynamically create proxy class objects. Naturally, a class loader is needed. //Parameter 2: the interface of the runtime class implementation of the proxy class object; Indicates what interface the proxy class needs to implement. //Parameter 3: the object of the class that implements the InvocationHandler interface; // The purpose of putting an object here is to: // The proxy class object created and returned dynamically by this method will automatically call invoke() in the handler no matter what method is called Solved the problem 3 // Therefore, the functions to be implemented by the proxy class should be placed in invoke(). return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler); } }
4. Method handler
public class MyInvocationHandler implements InvocationHandler { //obj: object of the proxied class private Object obj; public MyInvocationHandler(Object obj){ this.obj=obj; } //When we call method a through the object of the proxy class, we will automatically call the following method: invoke() //Declare the function of method a to be executed by the proxy class in invoke() /* * Parameter 1: proxy class object. There are proxy class objects first, and then call methods. * Parameter 2: method name called by proxy class object * Parameter 3: parameter of the method. For example, "hot pot" is passed in as a parameter * * */ @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. In essence, the method is called by the proxy class. Object returnValue = method.invoke(obj, args); //The return value of the method method is also the return value of the invoke() method return returnValue; } }
5. Testing
public class ProxyTest { public static void main(String[] args) { //Create proxied class object SuperMan superMan = new SuperMan(); //Get proxy class object Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); /* The proxy class object calls the method of the proxy class object: When called, it will automatically enter the invoke() in the handler. invoke(Object proxy, Method method, Object[] args) proxy It has been bound. method is eat and args is "hot pot". Then the method is called by the proxy class object in invoke(). * */ proxyInstance.eat("Hot Pot"); System.out.println(proxyInstance.getBelieve()); } }