Introduction to proxy Mode

The proxy pattern is a design pattern. As the name implies, it is the proxy of a thing, through which all external access or operations to the thing go. For example, if you have a legal dispute, you must go to a lawyer. For the lawyer, you are his client. For you, the lawyer is your agent.

In the programming world, we can describe the relationship between delegate classes and proxy classes

The role of the proxy mode: provides additional access to the delegate class, that is, through the proxy class to access the delegate class, so that you can provide additional functional operations without modifying the delegate class, thus extending the functionality of the delegate class.

In a nutshell, the proxy pattern enhances the delegate class by setting up an intermediate proxy to control all access to the delegate class.

In order to maintain consistency of behavior, proxy classes and delegate classes often implement the same interface, so there is no difference between them to visitors. Through the middle layer of proxy class, it can effectively control the direct access to the delegate class object, and also can hide and protect the delegate class object well. At the same time, it also reserves space for the implementation of different control strategies, so as to obtain greater flexibility in the design

Classification of proxy patterns

Before we go into the classification of proxy patterns, let’s prepare some classes and interfaces

    // The DataService interface provides the save method

    public interface DataService {

      public void save(a);

    }



    The DataServiceImpl class implements the DataService interface and overwrites the save method

    public class DataServiceImpl implements DataService{

      @Override

      public void save(a) {

        System.out.println("DataServiceImpl method save is called");

      }

    }

Copy the code

Static proxy (wrapping)

    / / the proxy class

    public class DataServiceProxy implements DataService{

        / / the delegate class

      DataService dataService = new DataServiceImpl();



      @Override

      public void save(a) {

        System.out.println("DataServiceProxy method save is called");

        dataService.save();

      }

    }

Copy the code

For the purposes of this code analysis, DataServiceProxy is the proxy class and DataServiceImpl is the delegate class

Advantages and disadvantages of static proxy:

  • advantages
    • You can extend the functionality of a delegate class without modifying it
  • disadvantages
    • The proxy class and the delegate class implement the same interface
    • Hardcoded, once the delegate class adds methods, the proxy class also needs to change

JDK dynamic proxy

Use JDK apis to dynamically build in-memory instance objects of proxy classes

A dynamic proxy class (hereinafter referred to as a proxy class) is a class that implements a list of interfaces specified at run time when the class is created and has the behavior described below:

  • A proxy interface is an interface implemented by a proxy class.
  • A proxy instance is an instance of a proxy class.
  • Each proxy instance has an associated InvocationHandler object that implements the interface InvocationHandler. A Method call on a proxy instance through one of the proxy interfaces will be assigned to the Invoke Method of the instance’s call handler, passing the proxy instance, a java.lang.Reflect. Method Object that identifies the calling Method, and an array of type Object containing the parameters. The invocation handler handles the encoded method call in an appropriate manner, and the result it returns is returned as the result of the method call on the proxy instance

To understand the mechanics of Java dynamic proxies, you first need to understand the following classes or interfaces:

  • java.lang.reflect.ProxyThis is the main class of Java’s dynamic proxy mechanism, which provides a set of static methods to dynamically generate proxy classes and their objects for a set of interfaces

Static method of Proxy

    // Method 1: This method is used to get the calling handler associated with the specified proxy object

    static InvocationHandler getInvocationHandler(Object proxy) 



    // Method 2: This method is used to get the class object associated with a dynamic proxy class that specifies a class loader and a set of interfaces

    static Class getProxyClass(ClassLoader loader, Class[] interfaces) 



    // Method 3: This method is used to determine whether the specified class object is a dynamic proxy class

    static boolean isProxyClass(Class cl) 



    // Method 4: This method is used to generate dynamic proxy class instances for the specified class loader, a set of interfaces, and the calling handler

    static Object newProxyInstance(ClassLoader loader, Class[] interfaces, 

        InvocationHandler h)


Copy the code
  • java.lang.reflect.InvocationHandlerThis is the call handler interface, which has a custom oneinvokeMethod that centrally handles method calls on dynamic proxy class objects, typically in which proxy access to delegate classes is implemented

The core method of InvocationHandler

    / * *

* This method is responsible for centrally handling all method calls on a dynamic proxy class. The first argument is both, and the second argument is

* proxy: proxy class instance

* method: The method object to be called

* args: call parameters

* The invocation handler preprocesses or dispatches execution to the delegate class instance based on these three parameters

* /


    Object invoke(Object proxy, Method method, Object[] args)

Copy the code

Each time a dynamic Proxy class object is generated, you need to specify a call handler object that implements this interface (see third parameter of Proxy static method 4).

  • java.lang.ClassLoaderThis is the classloader that loads the bytecode into the Java Virtual Machine (JVM) and defines class objects for it (Class) before the class can be used. throughProxy.newProxyInstanceThe generated dynamic proxy classes also need to be loaded by the class loader before they can be used.The only difference is that its bytecode is dynamically generated by the JVM at runtime rather than stored in some concrete.class file

Dynamic proxy mechanism and its characteristics

Let’s first look at how to use Java dynamic proxies. There are four steps as follows:

  1. By implementingInvocationHandlerInterface and implementationinvokeMethod to create your own call handler
  2. By providingProxyClass specifiedClassLoaderThe objects andA set of interfaceTo create dynamic proxy classes
  3. A constructor for a dynamic proxy class whose only argument type is the calling processor interface type is obtained through reflection
  4. A dynamic proxy class instance is created through a constructor that calls the processor object passed in as a parameter
    // InvocationHandlerImpl implements the InvocationHandler interface and dispatches method calls from the proxy class to the delegate class

    // It usually contains a reference to the delegate class instance inside, which is used to actually perform the method call dispatched forward

    InvocationHandler handler = newInvocationHandlerImpl(..) ;



    // Dynamically create a Proxy class object for a set of interfaces, including the Interface

    Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); 



    // Get the constructor object from the generated class object by reflection

    Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); 



    // Create a dynamic proxy class instance with the constructor object

    Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

Copy the code

The actual process is simpler, because the static method newProxyInstance of Proxy has encapsulated the process from Step 2 to Step 4 for us, so the simplified process is as follows

    // InvocationHandlerImpl implements the InvocationHandler interface and dispatches method calls from the proxy class to the delegate class

    InvocationHandler handler = newInvocationHandlerImpl(..) ;



    // Create a dynamic Proxy class instance directly through Proxy

    Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, 

         new Class[] { Interface.class }, 

         handler );

Copy the code

Specific code implementation:

Invoke handler class

    public class DataServiceInvovationHandler implements InvocationHandler {

      DataService dataService;



      public DataServiceInvovationHandler(DataService dataService) {

        this.dataService = dataService;

      }



      @Override

      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println(method.getDeclaringClass() + "" +  method.getName() + "() is called");

        method.invoke(dataService, args);

        return null;

      }

    }

Copy the code

Dynamic proxy class

    public class DataServiceDynamicProxy {

      DataService dataService = new DataServiceImpl();

      public void save(a) {

        DataService dataServiceDynamicProxy =

                (DataService) Proxy.newProxyInstance(dataService.getClass().getClassLoader(), new Class[]{DataService.class}, new DataServiceInvovationHandler(dataService));

        dataServiceDynamicProxy.save();

      }

    }

Copy the code

The differences between static and dynamic proxies are as follows:

  • The static proxy is implemented at compile time, when the proxy class is an actual class file
  • Dynamic proxies are dynamically generated at run time, meaning that there are no actual class files after compilation, but instead, class bytecodes are dynamically generated at run time and loaded into the JVM

Additional agent

Cglib (Code Generation Library) is a third party Code Generation class Library. The runtime dynamically generates a subclass object in memory to extend the function of the target object

Additional features

  • One limitation of the JDK’s dynamic proxy is that objects using dynamic proxies must implement one or more interfaces. If you want to proxy classes that do not implement interfaces, you can use the CGLIB implementation.
  • CGLIB is a powerful, high-performance code generation package that extends Java classes and implements Java interfaces at run time. It is widely used by many AOP frameworks, such as Spring AOP and Dynaop, to provide methods for theminterception(Intercept).
  • At the bottom of the CGLIB package is the ability to transform bytecode and generate new classes by using ASM, a small and fast bytecode processing framework. Direct use of ASM is discouraged because it requires familiarity with the JVM’s internal structures, including the class file format and instruction set.

The biggest difference between Cglib and dynamic proxies is this

  • Objects that use dynamic proxies must implement one or more interfaces
  • usecglibThe object of proxy does not need to implement interface, so as to achieve no intrusion of proxy class

Cglib jar: maven maven jar: maven jar: maven jar: maven JAR: maven JAR: MAVEN JAR

    <dependency>

        <groupId>cglib</groupId>

        <artifactId>cglib</artifactId>

        <version>3.2.5</version>

    </dependency>

Copy the code

Code implementation

Delegate class

    public class DataService {

      public void save(a) {

        System.out.println("class DataService method save is called");

      }

    }

Copy the code

The proxy class

    public class CglibProxy implements MethodInterceptor {

      private Object target; / / the delegate class



      public CglibProxy(Object target) {

        this.target = target;

      }



      // Generate a proxy object for the delegate class

      public Object getProxyInstance(a) {

        / / tools

        Enhancer enhancer = new Enhancer();

        // Set the delegate class as the parent

        enhancer.setSuperclass(target.getClass());

        // Set the callback function

        enhancer.setCallback(this);

        // Create a subclass object proxy

        return enhancer.create();

      }





      @Override

      public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println("DataService cglib proxy start...");

        // Execute delegate class methods through reflection

        method.invoke(target, objects);

        System.out.println("DataService cglib proxy end...");

        return null;

      }

    }

Copy the code

The test results

conclusion

  1. Static proxy implementation is simpler, as long as the proxy object wraps the target object to achieve enhanced functionality, but static proxy can only serve one target object, if there are too many target objects, many proxy classes will be generated.
  2. The JDK dynamic proxy requires the target object to implement the business interface; the proxy class only needs to implement itInvocationHandlerInterface.
  3. Static proxies are generated at compile timeclassBytecode file, can be directly used, high efficiency.
  4. Dynamic proxies must be implementedInvocationHandlerInterfaces, by reflecting proxy methods, consume more system performance but can reduce the number of proxy classes and be more flexible to use.
  5. cglibThe proxy does not need to implement an interface and implements the proxy by generating bytecode like, which is slightly faster than reflection and does not have a performance problem, butcglibWill inherit the target object, need to override the method, so the target object cannot befinalclass