Proxy mode of AOP in spring 07


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());
    }
}

Keywords: Java Design Pattern AOP reflection Proxy

Added by rationalrabbit on Mon, 31 Jan 2022 20:37:45 +0200