Java Dynamic Proxy

Posted by biscutty on Fri, 04 Feb 2022 07:03:33 +0100

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:

  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?

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.

Topics: Java