Proxy mode -JDK dynamic proxy

Follow the public account JavaStorm for more technology.

Provide a representation for another object to control customer access to the object. It is defined as providing a surrogate or placeholder for another object to access that object. You can visit github.com/UniqueDong/…

What is agency

Gobbledygook is a design pattern that is intended for code reuse. Instead of accessing objects directly, the proxy mode accesses our target objects and methods through proxies. Because sometimes we can’t contact the target object directly or we want to control client access. So we can access our real objects through the proxy.

Like “client” -> “star agent” -> “star”. We are not directly in contact with the star, the star is very busy, to sing, dance, perm film, the price is good enough, the agent told the star to accept the task.

The main character

  • Subject: The Subject interface that both the proxied class and the proxied class implement.
  • ConcreteSubject: proxyed class, also called delegate class, which is the real object that does the real work.
  • Proxy: Proxy class, acting as an intermediary, controls clients’ access to real objects and generates Proxy objects for clients to transparently invoke. Through Proxy, we can shield or process real objects.

Usage scenarios

  • It usually occurs in open source frameworks commonly used by us. For example, in Mybatis, we can call the logic to execute SQL through Mapper interface. In essence, dynamic proxy is used to locate the execution of real statements.
  • Spring AOP also makes use of dynamic proxy, such as Spring transaction, when the method is called by Spring transaction management, in fact, it will generate a proxy class, encapsulating our transaction processing logic to achieve transaction enhancement, code reuse. Free up the template code we need to start a transaction, execute logic, commit a transaction, or roll back a transaction, we can just write the roadbed code in peace. Here, the “template method pattern” and the dynamic proxy pattern are used. Refer to historical articles about the “template method” pattern.
  • There is also the Dubbo RPC framework, which uses dynamic proxies. To the consumer we can simply invoke the functionality implemented by the provider through the interface, just like local invocation. It encapsulates the tedious details of network transmission, serialization and decoding for us. A proxy class is generated to mask the low-level details. Allows us to call transparently.

We divide agents into static agents and dynamic agents according to the loading time of the proxied class. If we determine which class is proxied at compile time, we can use static proxies directly; If in doubt, you can use dynamic proxy mechanisms such as RPC frameworks and Spring AOP mechanisms that load the propped class while the code is running.

Examples of static proxy code

To implement a simple static proxy pattern using a UML class diagram, start by creating a Subject interface.

public interface Subject {

    public void request(a);
}

Copy the code

Create a real object to implement the interface

public class RealSubjbect implements Subject {

    @Override
    public void request(a) {
        System.out.println("this is RealSubjbect.request()"); }}Copy the code

Create our proxy class that holds references to real objects and implements the Subject interface.

public class ProxySubject implements Subject {

    private Subject realSubjbect = null;

    /** * In addition to acting for the real role to do what it should do, the agent role provides additional operations * such as */
    @Override
    public void request(a) {
        preRequest(); // The appended action before the actual role action

        if (realSubjbect == null) {
            realSubjbect = new RealSubjbect();
        }
        realSubjbect.request();

        postRequest();// Add action after real role action
    }
    /** * The additional operation before the actual role operation */
    private void postRequest(a) {
        System.out.println("Additional actions after real character actions.");

    }

    /** * The additional operation after the real role operation */
    private void preRequest(a) {
        System.out.println("Additional action before real character action"); }}Copy the code

Write our client interface, called by proxy class.

public class Client {

    public static void main(String[] args) {
        Subject subject = new ProxySubject();
        subject.request(); // Agents do things for real people}}Copy the code

The printed result is shown below. We have achieved control over the real object and added some new operations. Just as Spring’s AOP implementation does.

This is realSubjbect.request () The additional operation after the real role operationCopy the code

disadvantages

  1. An interface to a proxy object serves only one type of object. If you have many methods to proxy, you must proxy for each of them. Static proxies cannot do the job when the program is a little larger.
  2. If an interface adds a method, all proxy classes need to implement the method in addition to all implementation classes. Increases the complexity of code maintenance.

In addition, if the proxy pattern is to be used as described above, the real role (delegate class) must already exist as an internal property of the proxy object. However, in practice, a real role must correspond to a proxy role. If a large number of roles are used, the class will expand rapidly. Also, how do you use a proxy if you don’t know the real role (the delegate class) beforehand? This problem can be solved with Java’s dynamic proxy classes.

A dynamic proxy

concept

The source code for the dynamic proxy class is dynamically generated by the JVM based on reflection while the program is running, so there are no bytecode files for the proxy class. The relationship between the proxy class and the delegate class is determined at runtime.

implementation

There are two main implementations:

  1. Use JDK implementation. Delegate classes have interfaces in reverse order.
  2. It is implemented using CGLIB. Classes that cannot be final can only be public methods.

JDK dynamic proxy

The UML class diagram is shown below

  • Create a selfHandlerimplementationInvocationHandler, while holding references to real objects, will depend on our business class.
  • The proxy object passesProxy.newProxyInstance()Generate, and the method comes fromHandlerObject and real object.

We can also see from the class diagram that the invoke method of the Handler is delegated when called by proxy bytecode generated by the dynamic proxy. At the same time, the Handler holds the real business object. So you can control the behavior and access of the real object before you call it. Main advantages:

  • Hiding the implementation of the delegate class, the caller only needs to interact with the proxy class.
  • Uncoupling, doing extra processing without changing the delegate class code. Such as adding serialization, network transfer details when Dubbo client calls.

Code implementation

Using the class diagram, we can create a JDK dynamic proxy in three steps.

  1. Define proxied business interfaces and implementation classes.
  2. Create a custom InvocationHandler implementationInvocationHandlerInterface, and rely on proxied classes (references to real objects).
  3. throughProxy.newProxyInstance()Method to create a concrete proxy object.

Now we have a requirement to uniformly log call logging, or transaction control, for our business logic, so let’s write a log log for our proxyed class. Emulating Spring AOP-like functionality, a simplified version of the example gives you an idea of how it works and how it works.

Step 1: We define our own business interfaces OrderService and ProductService

public interface OrderService {
    String createOder(String no);
}

public interface ProductService {
    String getProduct(String no);
}
Copy the code

Implementation classes for business logic

public class OrderServiceImpl implements OrderService {
    @Override
    public String createOder(String no) {
        return "Generate order" + no + "Success"; }}public class ProductServiceImpl implements ProductService {
    @Override
    public String getProduct(String no) {
        return "Get goods" + no + "Success"; }}Copy the code

The second step: We implement the JDK’s Invocationhandler, which holds a delegate reference that defines an Object type. This will proxy all delegate classes that need logging, and we have a bind() method, Set target and return the corresponding proxy class for the client to call. Step 3 proxy.newproxyInstance () we put in bind. The code is much cleaner.

public class LoggerInterceptor implements InvocationHandler {

    /** * holds a reference to the delegate class */
    private Object target;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println(proxy.getClass().getInterfaces()[0]);
        // call by reflection
        System.out.println("Entered " + target.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
        // Call the target object's method
        Object result = method.invoke(target, args);
        long end = System.currentTimeMillis();
        System.out.println("Execution Result:" + result.toString());
        System.out.println("Total time:" + (end - start));
        return result;
    }

    /** * get the proxy class: and bind the Target delegate class to our defined Targe *@paramTarget The actual delegate class *@return* /
    public synchronized Object bind(Object target) {
        this.target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); }}Copy the code

Finally, we create a startup class to see the effect. There are two business logic classes that need logging. See the code comment for details

public class BootStrap {
    public static void main(String[] args) {
      // Create a business logic
        OrderService orderService = new OrderServiceImpl();
        ProductService productService = new ProductServiceImpl();
			// Create our log Handler
        LoggerInterceptor loggerInterceptor = new LoggerInterceptor();

      // Bind the delegate class and produce the proxy class invocation.
        OrderService orderServiceProxy = (OrderService) loggerInterceptor.bind(orderService);
        orderServiceProxy.createOder("12927381");

        ProductService productServiceProxy = (ProductService) loggerInterceptor.bind(productService);
        productServiceProxy.getProduct("34010234"); }}Copy the code

The result is printed as follows: We can see that each of our proxied interfaces prints logs and returns results before and after executing methods. Spring AOP is implemented through dynamic proxies.

nterface com.zero.headfirst.proxy.service.OrderService Entered Com. Zero. Headfirst. Proxy. Service. OrderServiceImpl - createOder, with the arguments {12927381} the execution result: generate order 12927381 successful time elapsed: 2 interface com.zero.headfirst.proxy.service.ProductService Entered Com. Zero. Headfirst. Proxy. Service. ProductServiceImpl - getProduct, with the arguments {34010234} the execution result: get goods 34010234 successful time elapsed: 1Copy the code

CGLIB dynamic proxy

CGLIB is a powerful high-performance code generation package. It is widely used by many AOP frameworks, such as Spring AOP’s interception of methods for them. 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.

Classes that cannot be final, and final methods, may not define interfaces.

The way to use it is simple: we first add a Cglib dependency and create a new class that implements MethodInterceptor.

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class LoggerProxy implements MethodInterceptor {


    /** * Create proxy class *@paramTargetClass Delegate class *@return* /
    public Object bind(Class targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        // Set the callback to call the intercept() method we overwrote when the client calls the method through the proxy
        enhancer.setCallback(this);
        // Create the proxy class
        return enhancer.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        long start = System.currentTimeMillis();
        // call by reflection
        System.out.println("Entered " + o.getClass().getName() + "-" + method.getName() + ",with arguments{" + args[0] + "}");
        Object result = methodProxy.invokeSuper(o, args);
        long end = System.currentTimeMillis();
        System.out.println("Execution Result:" + result.toString());
        System.out.println("Total time:" + (end - start));
        returnresult; }}Copy the code

Isn’t that easy? To create a new Enhancer, just set up the delegate class and the callback class. Here we can use the factory pattern to create callbacks for different proxy classes. I’m not going to write a simple example here.

Let’s look at our test class and the results

public class CglibBootStrap {
    public static void main(String[] args) {

        LoggerProxy loggerProxy = new LoggerProxy();

        OrderService orderService = (OrderService) loggerProxy.bind(OrderServiceImpl.class);
        orderService.createOder("12873051209g");


        ProductService productProxy = (ProductService) loggerProxy.bind(ProductServiceImpl.class);
        productProxy.getProduct("2780935782309"); }}Copy the code

The output is as follows

Entered com.zero.headfirst.proxy.service.OrderServiceImpl$$EnhancerByCGLIB$$54d983a1-createoder,with arguments{12873051209g} Result: The order 12873051209g is generated successfully. 14 Entered com.zero.headfirst.proxy.service.ProductServiceImpl$$EnhancerByCGLIB$$4e2c7c36-getProduct,with arguments{2780935782309} Result: 2780935782309 is obtained. Total time: 5Copy the code