V. agency mode

Keywords: Java JDK Spring

proxy pattern

Definition

Provides a proxy for other objects to control access to this object.

Why use agent mode?

  • Mediation isolation: in some cases, a client class does not want or can not directly reference a delegate object, while a proxy class object can mediate between the client class and the delegate object, which is characterized by that the proxy class and the delegate class implement the same interface.
  • Opening and closing principle, adding functions: in addition to being the intermediary between the customer class and the delegate class, we can also extend the function of the delegate class by adding additional functions to the delegate class. In this way, we only need to modify the proxy class without modifying the delegate class, which conforms to the opening and closing principle of code design. The agent class is mainly responsible for preprocessing messages, filtering messages, forwarding messages to the delegate class, and processing the returned results afterwards. The proxy class itself does not really implement the service, but it also calls the relevant methods of the delegate class to provide specific services. The real business function is implemented by the delegation class, but some public services can be added before and after the execution of the business function. For example, if we want to add caching and logging functions to the project, we can use the proxy class to complete it, without opening the encapsulated delegate class.

What are the agent models?

According to the period of agent creation, it can be divided into two types: static agent and dynamic agent. A static agent is created by a programmer or a specific tool that automatically generates the source code before compiling it. The agent class. Class file has been created before the programmer can run it. Dynamic agents are created dynamically by reflection mechanism when the program is running.

Static proxy

When using static proxy, you need to define the interface or parent class. The proxy object and proxy object implement the same interface or inherit the same parent class together.

Realization

Suppose Tom wants to buy a house, then he doesn't have enough money, so he asks his father to buy the house for him.

House buying interface:

public interface BuyHouse {
    void Buy();
}

Class Tom:

public class Tom implements BuyHouse {
    public void Buy() {
        System.out.println("Tom Got a house...");
    }
}

Class Father:

public class Father implements BuyHouse {
    private Tom tom;

    public Father(Tom tom){
        this.tom = tom;
    }
    
    public void Buy() {
        System.out.println("Father to Tom Bought a house...");
        tom.Buy();
    }
}

Advantages and disadvantages of static agents:

  • Advantage: it can extend the target function through proxy object without modifying the target object function
  • Disadvantages: because proxy objects need to implement the same interface as target objects, there will be many proxy classes; once the interface methods are added, both the target object and proxy object need to be maintained.

Dynamic proxy

  • The proxy object does not need to implement the interface, but the target object still needs to implement the interface, otherwise dynamic proxy cannot be used
  • The generation of proxy object is to build proxy object from JDK API dynamically

Let's use JDK dynamic proxy to override the static proxy example above

The house buying interface and Tom class are the same as the above

To create a proxy class, JdkProxy:

public class JdkProxy implements InvocationHandler {
    private Object target;
    //Receive the target object of the proxy
    public JdkProxy(Object target){
        this.target = target;
    }
    //Generate proxy objects for target objects
    public Object getProxyInstance(){
        Object instance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return instance;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("start Jdk Dynamic agent, to Tom Buy a house");
        //Method of executing target object
        Object returnVal = method.invoke(target,args);
        return returnVal;
    }
}

Test class:

public class testJdkProxy {
    @Test
    public void  test(){
        BuyHouse tom = new Tom();
        JdkProxy jdkProxy = new JdkProxy(tom);
        BuyHouse proxyInstance = (BuyHouse)jdkProxy.getProxyInstance();
        proxyInstance.Buy();
    }
}

/**test result
 Start the dynamic agent of Jdk to buy Tom a house
Tom Got a house
*/

Note that the Proxy.newProxyInstance() method takes three parameters:

  • ClassLoader loader: Specifies the classloader used by the current target object. The method to get the loader is fixed
  • Class <? >
  • InvocationHandler: Specifies the dynamic handler that fires the method of the event handler when the method of the target object is executed

Cglib agent

Both static proxy and JDK proxy require the target object to implement an interface, but sometimes the target object is only a single object and does not implement any object. At this time, you can use the subclass of the target object to implement the proxy, which is Cglib proxy.

CGLib uses a very low-level bytecode technology. Its principle is to create a subclass for a class through bytecode technology. In the subclass, the method interception technology is used to intercept all the calls of the parent class methods, and the crosscutting logic is weaved in accordance with the situation. However, because inheritance is used, the final decorated class cannot be proxied. Both JDK dynamic agent and CGLib dynamic agent are the foundation of Spring AOP.

Realization

To create a Tom class that does not implement an interface:

public class Tom {
    public void Buy() {
        System.out.println("Tom Got a house...");
    }
}

Create Cglib proxy class:

public class CglibProxy implements MethodInterceptor {
    private Object target;

    public Object getProxyInstance(Object target){
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("start Cglib Agent, here Tom Buy a house");
        Object returnVal = method.invoke(target, objects);
        return returnVal;
    }
}

To create a test class:

public class testCglibProxy {
    @Test
    public void test(){
        Tom proxyInstance = (Tom) new CglibProxy().getProxyInstance(new Tom());
        proxyInstance.Buy();
    }
}

/**test result
 Start Cglib agent to buy Tom a house
Tom Got a house
*/

CGLIB agent summary: CGLIB creates dynamic proxy objects with higher performance than JDK, but CGLIB takes much more time to create proxy objects than JDK. Therefore, for single instance objects, CGLIB is suitable, because there is no need to create objects frequently. On the contrary, JDK is more suitable. At the same time, because CGLIB uses the method of creating subclass dynamically, it can't proxy the final decorated method.

Posted by Tilemachos on Mon, 18 Nov 2019 05:43:40 -0800