Home  >  Article  >  Java  >  Example analysis of Java dynamic proxy and static proxy

Example analysis of Java dynamic proxy and static proxy

PHPz
PHPzforward
2023-05-06 23:46:06934browse

1. Static proxy

Use of static proxy

Static proxy, the proxy class and the proxied class implement the same interface, and the proxy class also holds a reference to the proxied class. In this way, when we need to call the method of the proxy class, we can do it by calling the method of the proxy class.

Example: Suppose the leader’s job is to hold meetings and evaluate employees.

First define an interface:

package com.sharpcj;
public interface IWork {
    void meeting();
    int evaluate(String name);
}

Then define the leadership class:

package com.sharpcj;
import java.util.Random;
public class Leader implements IWork {
    @Override
    public void meeting() {
        System.out.println("领导早上要组织会议");
    }
    @Override
    public int evaluate(String name) {
        int score = new Random(System.currentTimeMillis()).nextInt(20) + 80;
        System.out.println(String.format("领导给%s的考评为%s分", name, score));
        return score;
    }
}

Secretary class:

package com.sharpcj;
public class Secretary implements IWork {
    private Leader mLeader;
    public Secretary(Leader mLeader) {
        this.mLeader = mLeader;
    }
    @Override
    public void meeting() {
        System.out.println("秘书先给老板准备材料");
        mLeader.metting();
    }
    @Override
    public int evaluate(String name) {
        return mLeader.evaluate(name);
    }
}

Test class:

package com.sharpcj;
public class TestApp {
    public static void main(String[] args) {
        Leader leader = new Leader();
        Secretary secretary = new Secretary(leader);
        secretary.meeting();
        secretary.evaluate("Joy");
    }
}

Execution result:

Example analysis of Java dynamic proxy and static proxy

This code It's very simple. Note that when calling the meeting method of the Secretary class, we called the meeting method of the Leader class. Before that, we also expanded this method. At this point, some people may be confused. This seems to be a bit of a decorator pattern. What's the matter?

Differences from the decorator pattern

In fact, there are still many differences between the decorator pattern and the proxy pattern. The Decorator pattern focuses on dynamically adding methods on an object, whereas the Proxy pattern focuses on controlling access to the object. In other words, using the proxy pattern, a proxy class can hide the specific information of an object from its clients. Therefore, when using the proxy pattern, we often create an instance of an object in a proxy class. And, when we use the decorator pattern, our usual approach is to pass the original object as a parameter to the decorator's constructor.

We can summarize these differences in another sentence: using the proxy pattern, the relationship between the proxy and the real object is usually determined at compile time, while the decorator can be recursive at runtime. Ground cover structure.

Let’s first look at the difference between the UML class diagrams of the two:

Agent mode:

Example analysis of Java dynamic proxy and static proxy

Decorator mode:

Example analysis of Java dynamic proxy and static proxy

Both pseudocode:

Agent mode:

Interface Subject {
    void doAction()
}
public class RealSubject implements Subject{
    @Override
    public void doAction() {};
}
public class Proxy implements Subject{
       private RealSubject realSubject;

       public Proxy(RealSubject realSubject) {
             //关系在编译时确定
            this.realSubject = realSubject;
       }
       @Override
       public void doAction() {
             ….
             realSubject.doAction();
             ….
       }
}

Decorator pattern;

Interface Component {
    void doAction()
}
public class ConcreteComponent implement Component {
    @Override
    public void doAction() {};
}
public class Decorator implements Component {
       private Component component;

       public Decorator(Component component) {
             //关系在编译时确定
            this.component = new component;
       }
       public void doAction() {
             ….
             component.doAction();
             ….
       }
}

In fact, the focus of proxy mode and decorator mode are different. The focus of proxy mode is to clarify the class being proxied. As in the above example, the secretary clearly wants to act for the leader. The decorator pattern focuses on methods of extending classes. The objects of classes that implement the Component interface held by the decorated class are not fixed. That is to say, the decorated class can decorate any object that implements the Component interface based on the parameters passed in during the call. the type.

2. Dynamic proxy

Dynamic proxy can be divided into JDK dynamic proxy and CGlib dynamic proxy based on different implementation methods.

JDK dynamic proxy: Use the reflection mechanism to generate a class that implements the proxy interface, and call InvokeHandler to process it before calling the specific method.
CGlib dynamic proxy: Use ASM (open source Java bytecode editing library, operating bytecode) open source package to load the class file of the proxy object class and generate it by modifying its bytecode subclass to handle.
Difference: JDK agent can only generate agents for classes that implement interfaces; CGlib implements agents for classes, generates a subclass for the specified class, and overrides the methods in it. This is done by inheriting the class The implementation method cannot proxy the final modified class.

JDK Dynamic Proxy

Let’s take the above example as an example:
First, define a class to implement the InvocationHandler interface and implement invoke Method:

package com.sharpcj;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WorkInvocationHandler implements InvocationHandler {
    private Object object;
    public WorkInvocationHandler(Object object) {
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("object: " + object.getClass().getSimpleName());
        System.out.println("proxy: " + proxy.getClass().getSimpleName());

        if ("meeting".equals(method.getName())) {
            System.out.println("代理先准备会议材料...");
            return method.invoke(object, args);
        } else if ("evaluate".equals(method.getName())) {
            if(args[0] instanceof String) {
                if ("James".equals(args[0])) {
                    System.out.println("James 犯过错误,所以考评分数较低...");
                    return 70;
                }
            }
            return method.invoke(object, args);
        }
        return null;
    }
}

Then create a proxy object through the Proxy.newProxyInstance() method:

package com.sharpcj;
import java.lang.reflect.Proxy;
public class TestApp {
    public static void main(String[] args) {
        /*Leader leader = new Leader();
        Secretary secretary = new Secretary(leader);
        secretary.meeting();
        secretary.evaluate("Joy");*/
        Leader leader = new Leader();
        IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(),
                new Class[]{IWork.class}, new WorkInvocationHandler(leader));
        proxy.meeting();
        proxy.evaluate("Joy");
        proxy.evaluate("James");
    }
}

Output result:

Example analysis of Java dynamic proxy and static proxy

We see that through the WorkInvocationHandler class, we can also proxy the implementation of the methods of the Leader class. In fact, what we implement is the implementation of any method, but we just pass it in when we create the proxy object. It is the Iwork interface and Leader class object.

What needs to be noted here is: The first parameter proxy in the invoke method of the InvocationHandler interface is not the object of our calling method. So what is this parameter? In the code, I specially added corresponding printing to print out the class name of proxy. In fact, proxy is the proxy object itself. Its meaning is that we can return the proxy object in the invoke method and then make continuous calls.

Look at the following example:

package com.sharpcj.proxytest;
public interface IWork {
    IWork work(String subject);
}
package com.sharpcj.proxytest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class WorkInvocationHandler implements InvocationHandler {
    private Object object;
    public WorkInvocationHandler(Object object) {
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("work".equals(method.getName())){
            System.out.println("--- work: " + args[0]);
            return proxy;
        }
        return null;
    }
}
package com.sharpcj.proxytest;
import java.lang.reflect.Proxy;
public class TestApp {
    public static void main(String[] args) {
        IWork worker = (IWork) Proxy.newProxyInstance(IWork.class.getClassLoader(), new Class[]{IWork.class},
                new WorkInvocationHandler(new IWork() {
                    @Override
                    public IWork work(String subject) {
                        return null;
                    }
                }));
        worker.work("AAA").work("BBB").work("CCC");
    }
}

The result is as follows:

Example analysis of Java dynamic proxy and static proxy

CGlib 动态代理实现

首先添加 cglib 依赖

build.gradle 文件:

... 
dependencies {
    // 引入 cglib 库
    compile 'cglib:cglib:3.1'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}
...

前面说了,cglib 针对类进行代理,我们以上面的 Leader 类为例,先创建一个类实现 MethodInterceptor接口:

package com.sharpcj;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class LeaderMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        if ("meeting".equals(method.getName())) {
            System.out.println("代理先准备会议材料...");
            return methodProxy.invokeSuper(o, objects);
        } else if ("evaluate".equals(method.getName())) {
            if(objects[0] instanceof String) {
                if ("James".equals(objects[0])) {
                    System.out.println("James 犯过错误,所以考评分数较低...");
                    return 70;
                }
            }
            return methodProxy.invokeSuper(o, objects);
        }
        return null;
    }
}

测试代码:

package com.sharpcj;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
import java.lang.reflect.Proxy;
public class TestApp {
    public static void main(String[] args) {
        // System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\temp\code");  //保存生成的 class 文件
        Enhancer enhancer = new Enhancer(); // 通过CGLIB动态代理获取代理对象的过程
        enhancer.setSuperclass(Leader.class); // 设置enhancer对象的父类
        enhancer.setCallback(new LeaderMethodInterceptor()); // 设置enhancer的回调对象
        Leader proxy= (Leader)enhancer.create(); // 创建代理对象

        // 通过代理对象调用目标方法
        proxy.meeting();
        proxy.evaluate("Joy");
        proxy.evaluate("James");
    }
}

结果如下:

Example analysis of Java dynamic proxy and static proxy

MethodInterceptor 接口只有一个 intercept 方法,这个方法有4个参数:

  • 1)obj表示增强的对象,即实现这个接口类的一个对象;

  • 2)method表示要被拦截的方法;

  • 3)args表示要被拦截方法的参数;

  • 4)proxy表示要触发父类的方法对象;

需要注意的是:实际调用是 methodProxy.invokeSuper(), 如果使用 invoke() 方法,则需要传入被代理的类对象,否则出现死循环,造成 stackOverflow 。

The above is the detailed content of Example analysis of Java dynamic proxy and static proxy. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete