MyBtis - Dynamic Agent

Keywords: JDK Java Attribute

Articles Catalogue

concept

According to the period of the establishment of agency, there are two types of agency:

Static: Programmers create proxy classes or specific tools to automatically generate source code and compile it. The. class file of the proxy class already exists before the program runs.

Dynamics: When the program runs, it is created dynamically by using reflection mechanism.

Static proxy

Let's look at the implementation of static proxy first.

/**
 * @ClassName Star
 * @Description Common interface
 * @Author lzq
 * @Date 2019/8/1 10:39
 * @Version 1.0
 **/
public interface Star {
    public void sell();   //Selling goods
}
/**
 * @ClassName RealStar
 * @Description True object delegate class
 * @Author lzq
 * @Date 2019/8/1 10:39
 * @Version 1.0
 **/
public class RealStar implements Star{
    @Override
    public void sell() {
        System.out.println("Selling goods");
    }
}
/**
 * @ClassName StarProxy
 * @Description proxy class
 * @Author lzq
 * @Date 2019/8/1 10:38
 * @Version 1.0
 **/
public class StarProxy implements Star{
    private RealStar realStar = new RealStar();

    @Override
    public void sell() {
        realStar.sell();
    }
}

To test the code, we only need to interact with the delegate class, regardless of its underlying implementation:

/**
 * @ClassName Test
 * @Description test
 * @Author lzq
 * @Date 2019/8/1 11:08
 * @Version 1.0
 **/
public class Test {
    public static void main(String[] args) {
        StarProxy proxy = new StarProxy();
        proxy.sell();
    }
}

Operation results:

Selling goods

Actually, dynamic proxy is the same principle, but the way it creates objects is through reflection.

Dynamic Agent

In Java, there are many technologies to implement dynamic proxy, such as JDK, CGLIB, Javassist, ASM and so on. The most common ones are JDK and CGLIB, which are explained in detail below.

JDK Dynamic Agent

When we use static proxy, we define a common interface, then let the delegate class and the proxy class implement the interface separately (the proxy object and the real object are all hanging under the common interface later), and finally define an object of the delegate class in the proxy class, through the bottom layer. To invoke the corresponding method of the delegate class to realize the agent logic is the same in the dynamic proxy. It also needs a common interface, a delegate class that has been implemented, but the difference is that its proxy class is generated dynamically according to the interface and the delegate class.

First we define an interface HelloWorld:

/**
 * @ClassName HelloWorld
 * @Description Common interface
 * @Author lzq
 * @Date 2019/8/1 11:26
 * @Version 1.0
 **/
public interface HelloWorld {
    public void sayHelloWorld();
}

Define a delegate class to implement this interface:

/**
 * @ClassName HelloWorldImpl
 * @Description Delegate class real object class
 * @Author lzq
 * @Date 2019/8/1 11:27
 * @Version 1.0
 **/
public class HelloWorldImpl implements HelloWorld{
    @Override
    public void sayHelloWorld() {
        System.out.println("Hello World");
    }
}

In JDK dynamic proxy, to realize proxy logic, we must implement java.lang.reflect.InvocationHandler interface, which defines an invoke method and provides an array of interfaces for hanging proxy objects. The following class mainly implements dynamic proxy binding and proxy logic implementation:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 1. @ClassName JDKProxyExample
 2. @Description Dynamic proxy logic code
 3. @Author lzq
 4. @Date 2019/8/1 11:28
 5. @Version 1.0
 **/
public class JDKProxyExample implements InvocationHandler {
    private Object object = null;  //True object delegate class object

    /**
     * Get the proxy object
     * @param o
     * @return
     */
    public Object bind(Object o) {
        this.object = o;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),o.getClass().getInterfaces(),this);
    }

    /**
     * Agent Method Logic
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Processing before entering proxy logic...");
        System.out.println("Services before calling real objects...");
        Object obj = method.invoke(object,args);
        System.out.println("Services after calling real objects...");
        return obj;
    }
}

In this class, we need:

1. Establishing the relationship between the agent object and the real object

In this class, we provide the implementation of the bind method, which first saves the real object with the attribute object of the class, and then builds and generates the proxy object through the following code:

Proxy.newProxyInstance(object.getClass().getClassLoader(),o.getClass()
.getInterfaces(),this);

These three parameters mean:

  • The first is the class loader, which class loader is used to load the proxy object. Here we use the class loader to load the real object's class.
  • The second is to hang the generated proxy objects under which interfaces, which is written under the interface implemented by the object. The interface of the HelloWorld Impl object is obviously HelloWorld.
  • The third is to define the proxy class of implementing method logic. this represents the current object. It must implement invoke method in InvocationHandler interface, which is the implementation method of proxy logic method.

2. Implementing Agent Logic Method

The invoke method implements proxy logic. Its three parameters are as follows:

  • Proxy, the proxy object, is the object generated by the bind method.
  • Method, the current scheduling method;
  • args, parameters of scheduling method;

When we use the proxy object scheduling method, it goes into the invoke method:

Object obj = method.invoke(object,args);

This line of code is equivalent to the method of scheduling real objects, but it is only implemented by launching.

The relationship between these parts is equivalent to:

  • Proxy is equivalent to business object and proxy object.
  • Object is equivalent to Software Engineer object, real object and client.
  • The method of bind is to establish the agency relationship between business and software engineers.
  • invoke is the business logic that controls the access of software engineers.

The test code is as follows:

/**
 * @ClassName Test
 * @Description Test code
 * @Author lzq
 * @Date 2019/8/1 11:32
 * @Version 1.0
 **/
public class Test {
    public static void main(String[] args) {
        JDKProxyExample jdk = new JDKProxyExample();
        HelloWorld proxy = (HelloWorld)jdk.bind(new HelloWorldImpl());
        proxy.sayHelloWorld();
        proxy.sayXXX();
    }
}

So what are the benefits of dynamic proxy? Because its proxy objects are generated dynamically by reflection, if new functions are needed, only a method is added to the public interface, and the real object can implement the method. Dynamic proxy binding and proxy logic need not be moved at all.

Test code run results:

CGLIB Dynamic Agent

JDK dynamic proxy can only be used by providing a common interface. In some environments where no interface can be provided, other third-party technologies can only be used, such as CGLIB dynamic proxy. The advantage of JDK dynamic proxy is that it does not need to provide a common interface, and only a non-abstract class can realize dynamic proxy.

CGLIB dependencies:

        <!--CGlib Dynamic Agent-->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.2</version>
        </dependency>

The code is as follows:

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

import java.lang.reflect.Method;

/**
 * @ClassName CglibProxyExample
 * @Description
 * @Author lzq
 * @Date 2019/8/1 12:12
 * @Version 1.0
 **/
public class CglibProxyExample implements MethodInterceptor {
    /**
     * Generating CGLIB proxy objects
     * @param cls
     * @return
     */
    public Object getProxy(Class cls) {
        //CGLIB Enhancer Enhances Class Objects
        Enhancer enhancer = new Enhancer();
        //Setting Enhancement Types
        enhancer.setSuperclass(cls);
        //Take you a proxy logical object as the current object, which requires the current object to implement MethodInterceptor's method intercept
        enhancer.setCallback(this);
        //Generate and return proxy objects
        return enhancer.create();
    }

    /**
     * Agent Logic Method
     * @param o  Proxy object
     * @param method  Method
     * @param objects  Method parameters
     * @param methodProxy  Method Agent
     * @return  Proxy Logic Return
     * @throws Throwable abnormal
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Processing logic before calling real objects...");
        Object result=  methodProxy.invokeSuper(o,objects);
        System.out.println("Processing logic after calling real objects");
        return result;
    }
}

Enhancer class of CGLIB is used here. By setting Superclass, which class is its proxy class is set by setCallback method. The parameter this means the current object. This object is required to implement the method of interface MethodInterceptor, intercept, and then return. Agent object;

At this time, the intercept method of the current class is the proxy logic method, whose parameter meaning can be seen in the code annotation.

Write a real object class.

/**
 * @ClassName Test
 * @Description Delegate class real object class
 * @Author lzq
 * @Date 2019/8/1 12:28
 * @Version 1.0
 **/
public class Test {
    public void say(String x) {
        System.out.println(x);
    }
}

Test code:

   public static void main(String[] args) {
        CglibProxyExample cpe = new CglibProxyExample();
        Test test = (Test)cpe.getProxy(Test.class);
        test.say("Hello");
    }

Operation results:

Actually, the implementation of dynamic proxy is very similar. They all use getProxy method to generate proxy objects and formulate proxy logic classes. The proxy logic class implements a method of an interface. Then the method of interface definition is proxy logic method, which can control the method of real objects.

The difference between JDK dynamic proxy and CGLIB dynamic proxy:

In the dynamic proxy of JDK, we can see that:


Then it generates proxy objects and real objects at the same level, which are implementation classes under the common interface, and they are horizontal relations.

Let's look at CGLIB's:

Its principle is to generate a proxy object to intercept the real object, but the proxy object is a subclass of the real object, inheritance relationship, so in CGLIB, the proxy object is a subclass of the real object, they are vertical relations.

Posted by shann on Fri, 06 Sep 2019 06:47:35 -0700