Java | What is Dynamic Proxy?

  Agent mode, cglib, java

WeChat Public Number: An Outstanding Disabled Person. If you have any questions, please leave a message backstage. I won’t listen anyway.

Recently, I reviewed Java and reviewed the lower proxy mode. Proxy mode has been applied in many places in Java field. It is divided into static proxy and dynamic proxy. Spring AOP is a typical example of dynamic proxy. Dynamic agents are also divided into interface agents and cglib (subclass agents). According to my understanding, I wrote several demo to share with you. This is the article written from last night’s immortal cultivation to 3 o’clock. I don’t think it can be said.

Agent mode is very common in our daily life, and there are agents everywhere:

  • It is very difficult to rob tickets for Zhang Xueyou’s concert. You can find scalpers to queue up and buy them.
  • If you think it is troublesome to go out for dinner, you can order takeout.

Both cattle and take-out riders have to help us with our work. But they can’t do everything by themselves (for example, scalpers can’t help me eat). They can only do things that we can’t or don’t want to do.

  • Looking for scalpers can help me queue up to buy tickets for Zhang Xueyou’s concert.
  • Take-out riders can help me take the food downstairs.

So, look. The proxy mode is actually what the current object does not want to do and entrusts it to other objects.

Static proxy

I will write a demo explanation, taking the example of looking for scalpers to help me queue up to buy tickets for Zhang Xueyou’s concert. Now there is a Human interface, both I and scalpers have implemented this interface.

public interface Human {

    void eat();

    void sleep();

    void lookConcert();

}

For example, in my class, I will eat and sleep, such as the following classes:

public class Me implements Human{

    @Override
    public void eat() {
        System.out.println("eat emat ....");
    }

    @Override
    public void sleep() {
        System.out.println("Go to bed at one o'clock in the morning");
    }

    @Override
    public void lookConcert() {
        System.out.println("Listen to Jacky Cheung's Concert");
    }

}

There are yellow cattle, such as:

public class Me implements Human{

    @Override
    public void eat() {
    }

    @Override
    public void sleep() {
    }

    @Override
    public void lookConcert() {
    }

}

Now that I and the scalpers are ready, how can I relate the two? What we want to make clear is that scalpers want to help me buy tickets, so buying tickets will necessarily require me to queue up, so there are the following scalpers: Note that we don’t care here, other behaviors of scalpers, we only care about whether he can queue up to buy tickets.

public class HuangNiu implements Human{

    private Me me;

    public HuangNiu() {
        me = new Me();
    }

    @Override
    public void eat() {
    }

    @Override
    public void sleep() {
    }

    @Override
    public void lookConcert() {
        // 添加排队买票方法
        this.lineUp();
        me.lookConcert();
    }

    public void lineUp() {

        System.out.println("line up");

    }

}

The final main method call is as follows:

public class Client {

    public static void main(String[] args) {

        Human human = new HuangNiu();
        human.lookConcert();

    }

}

The results are as follows:

静态代理结果

From this, it can be seen that scalpers just did what we didn’t want to do (queuing up to buy tickets). I was the one who actually watched the concert. The client also does not care which class the proxy class proxies because the code controls the client’s access to the proxy class. The client code is represented as humanhuman = newhuanniu ();

Because proxy classes implement interfaces of abstract roles, proxy classes cannot be used universally. For example, my dog is ill and wants to see a doctor, but it is very troublesome to register in line. I also want to have a scalper to help me register in line to see a doctor, but the scalper doesn’t understand the characteristics of this dog (the scalper is not the same type as the dog, and the scalper belongs to Human but the dog belongs to Animal). However, it is one thing to register in line and to buy tickets in line compared with the scalper. This method is the same and queues at the scene. Can we find an agent to say that we can help people queue up to buy tickets or dogs to register?

The answer is definitely yes, dynamic proxy can be used.

Dynamic Proxy Based on Interface

As described in the content of static agents, static agents are limited by the implementation of interfaces. Dynamic proxy is to use reflection to dynamically obtain the type of abstract interface, thus obtaining relevant characteristics to proxy. Because dynamic proxy can proxy for all principals, it gives the proxy class a common name: HuangNiuHandle. Let’s look at what cattle can become first.

public class HuangNiuHandle implements InvocationHandler {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
    }

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

        Object methodObject = null;

        System.out.println("line up");
        methodObject = method.invoke(proxyTarget, args);
        System.out.println("go home and sleep");

        return methodObject;
    }

}

This time the client code becomes like this

public class Client {

    public static void main(String[] args) {

        HuangNiuHandle huangNiuHandle = new HuangNiuHandle();
        Human human = (Human) huangNiuHandle.getProxyInstance(new Me());

        human.eat();
        human.run();
        human.lookConcert();

        System.out.println("------------------");

        Animal animal = (Animal) huangNiuHandle.getProxyInstance(new Dog());
        animal.eat();
        animal.run();
        animal.seeADoctor();
    }

}

There are three main points in using dynamic agents.

  1. The InvocationHandler interface must be implemented to indicate that the class is a dynamic proxy execution class.
  2. There is an implementation method in the InvocationHandler interface as follows: publicobjectinvoke (objectproxy, methodmethod, object [] args). You need to override this method when using it.

    1. To obtain a proxy class, you need to use proxy.newproxystance (classloader, class <? > [] interfaces, invocation handler h) this method to get Proxy objects (instances of Proxy class types).

Notice the Proxy.newProxyInstance method, which needs to pass in 3 parameters. Resolution is as follows:

// 第一个参数,是类的加载器
// 第二个参数是委托类的接口类型,证代理类返回的是同一个实现接口下的类型,保持代理类与抽象角色行为的一致
// 第三个参数就是代理类本身,即告诉代理类,代理类遇到某个委托类的方法时该调用哪个类下的invoke方法
Proxy.newProxyInstance(Class loader, Class<?>[] interfaces, InvocationHandler h)

Let’s take a look at the invoke method. What method the user calls the proxy object is essentially calling the processor.
The invoke method, through which the target method is called, also has three parameters:

// 第一个参数为 Proxy 类类型实例,如匿名的 $proxy 实例
// 第二个参数为委托类的方法对象
// 第三个参数为委托类的方法参数
// 返回类型为委托类某个方法的执行结果
public Object invoke(Object proxy, Method method, Object[] args)

The output after calling the proxy class:

动态代理

According to the results, scalpers not only helped me to queue up to buy tickets, but also helped my dog to register. Therefore, you see that static agents need to write their own proxy classes (proxy classes need to implement the same interface as the target object) and also need to implement interface methods one by one, but dynamic agents do not need to.

Note that not all of our methods require scalpers as agents to queue up. We know that scalpers are only needed when I go to a concert and my dog goes to see a doctor. If we want to add a specific proxy to the method we want, we can obtain the method object method name through the method reflection in the invoke method, so the dynamic proxy class can become like this:

public class HuangNiuHandle implements InvocationHandler {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(), proxyTarget.getClass().getInterfaces(), this);
    }

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

        Object methodObject = null;

        if ("lookConcert".equals(method.getName()) ||
        "seeADoctor".equals(method.getName())) {

            System.out.println("line up");
            // 调用目标方法
            methodObject = method.invoke(proxyTarget, args);
        } else {
            // 不使用第一个proxy参数作为参数,否则会造成死循环
            methodObject = method.invoke(proxyTarget, args);
        }

        return methodObject;
    }

}

The results are as follows: We can see that we only turned to scalpers in specific ways.

动态代理

It can be seen from this that dynamic agents are generally used in logging and other horizontal services.

It is worth noting that:

  1. The dynamic proxy mode based on interface class must have three basic roles: abstract role, proxy class and proxy. Delegate classes and proxy classes must be derived from abstract roles, otherwise the schema cannot be used.
  2. The dynamic proxy mode finally returns objects with abstract roles (top-level interfaces). Methods that are critically decorated by private or protected within a delegate class will not be called, even if they are allowed to be called. It is also impossible to use proxy classes to convert to subclass interfaces to call methods on the client. That is to say, the dynamic proxy mentioned above returns the Human or Animal of the delegate class (Me) or (Dog).
  3. Why not use the first parameter to perform a callback within the invoke method. When the client uses getProxyInstance(new Child ()), JDK will return an instance of proxy with InvokecationHandler object and dynamically inherited target. The client called the target method with the following operations: JDK first looks up the handler object in the proxy instance and then executes the invoke method in the handler.

According to the public Object invoke method, the first parameter proxy corresponds to a proxy instance. If method.invoke(proxy,args) is used in invoke, such a chain of methods will appear, target method →invoke→ target method →invoke …, eventually leading to stack overflow.

Dynamic Proxy Based on Subclass

In order to save trouble, I do not inherit the parent class here, but in actual development it is necessary to inherit the parent class to be more convenient to expand. Unlike interface-based implementation classes:

  1. CGLib (Dynamic Proxy Based on Subclass) uses MethodInterceptor, which needs to import cglib.jar and asm.jar packages
  2. A dynamic proxy based on subclasses returns subclass objects
  3. The method interceptor can call the protected decorated method.

The code is as follows:

public class Me {

    public void eat() {
        System.out.println("eat meat ....");
    }

    public void run() {
        System.out.println("I run with two legs");
    }

    public void lookConcert() {
        System.out.println("Listen to Jacky Cheung's Concert");
    }

    protected void sleep() {
        System.out.println("Go to bed at one o'clock in the morning");
    }

}

Dog class

public class Dog {

    public void eat() {
        System.out.println("eat Dog food ....");
    }

    public void run() {
        System.out.println("Dog running with four legs");
    }

    public void seeADoctor() {
        System.out.println("The dog go to the hospital");
    }

}

Yellow cattle proxy class, notice that invoke () has an additional parameter methodProxy here, which is used to execute the method of the target (proxy class). as for why methodProxy is used, the official explanation is that it is fast and does not need to save the reference of the proxy object when calling the method of the proxy class within intercep t t.

public class HuangNiuHandle implements MethodInterceptor {

    private Object proxyTarget;

    public Object getProxyInstance(Object target) {
        this.proxyTarget = target;
        return Enhancer.create(target.getClass(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {

        Object methodObject = null;

        if ("lookConcert".equals(method.getName()) ||
                "seeADoctor".equals(method.getName())) {
            System.out.println("line up");
            // 调用目标方法
            methodObject = methodProxy.invokeSuper(proxy, args);
        } else {
            methodObject = method.invoke(proxyTarget, args);
        }

        return methodObject;
    }
}

Client class

public class Client {

    public static void main(String[] args) {
        HuangNiuHandle huangNiuHandle = new HuangNiuHandle();
        Me me = (Me) huangNiuHandle.getProxyInstance(new Me());

        me.eat();
        me.run();
        me.sleep();
        me.lookConcert();

        System.out.println("------------------");

        Dog dog = (Dog) huangNiuHandle.getProxyInstance(new Dog());
        dog.eat();
        dog.run();
        dog.seeADoctor();
    }
}

Results:

基于子类的动态代理

Note that the protected-decorated Method sleep in the me class can still be called by the client. This is not allowed in an interface-based dynamic proxy.

The difference between static agent and dynamic agent

Static agents need to write their own proxy classes and implement the target methods one by one, and the proxy classes must implement the same interface as the target objects.

Dynamic proxy does not need to implement proxy classes by itself. It uses JDKAPI to dynamically build proxy objects in memory (we need to pass in proxy classes) and implements all target methods by default.

Source code download:https://github.com/turoDog/re …

Postscript

If this article is of any help to you, please help me look good. Your good looks are my motivation to keep writing. Pay attention to the public number, an excellent invalid reply1024Access to information:Python, C++, Java, Linux, Go, Front End, Algorithm Data Sharing

一个优秀的废人,给你讲几斤技术。