Maison  >  Article  >  Java  >  Exemple d'analyse du proxy dynamique Java et du proxy statique

Exemple d'analyse du proxy dynamique Java et du proxy statique

PHPz
PHPzavant
2023-05-06 23:46:06932parcourir

1. Proxy statique

Utilisation d'un proxy statique

Le proxy statique, la classe proxy et la classe proxy implémentent la même interface, la classe proxy contient également une référence à la classe proxy, donc lorsque nous devons appeler la méthode de la classe proxy , vous pouvez le faire en appelant la méthode de la classe proxy.

Exemple : Supposons que le travail du leader consiste à organiser des réunions et à évaluer les employés.

Définissez d'abord une interface :

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

Puis définissez la classe de leadership :

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;
    }
}

Classe de secrétaire :

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);
    }
}

Classe de test :

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");
    }
}

Résultat d'exécution :

Exemple danalyse du proxy dynamique Java et du proxy statique

Ceci code C'est très simple. Notez que lors de l'appel de la méthode de réunion de la classe Secretary, nous avons appelé la méthode de réunion de la classe Leader. Avant cela, nous avons également développé cette méthode. À ce stade, certaines personnes peuvent être confuses. Cela semble être un peu un modèle de décorateur. Que se passe-t-il? Secretary类的 meeting 方法时,我们调用了Leader类的 meeting 的方法,在此之前,我们还扩充了该方法。这时有的人可能有疑惑了,这看起来有点是装饰者模式了。这到底怎么回事?

与装饰者模式的区别

实际上,在装饰器模式和代理模式之间还是有很多差别的。装饰器模式关注于在一个对象上动态的添加方法,然而代理模式关注于控制对对象的访问。换句话说,用代理模式,代理类(proxy class)可以对它的客户隐藏一个对象的具体信息。因此,当使用代理模式的时候,我们常常在一个代理类中创建一个对象的实例。并且,当我们使用装饰器模式的时候,我们通常的做法是将原始对象作为一个参数传给装饰者的构造器。

我们可以用另外一句话来总结这些差别:使用代理模式,代理和真实对象之间的的关系通常在编译时就已经确定了,而装饰者能够在运行时递归地被构造。

先看看两者的 UML 类图区别:

代理模式:

Exemple danalyse du proxy dynamique Java et du proxy statique

装饰者模式:

Exemple danalyse du proxy dynamique Java et du proxy statique

两者伪代码:

代理模式:

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();
             ….
       }
}

装饰者模式;

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();
             ….
       }
}

其实代理模式和装饰者模式侧重点不一样,代理模式重点在于明确了被代理的类。如上例中,秘书很明确要代理的是的领导。而装饰者模式侧重于拓展类的方法,装饰类持有的实现Component接口的类的对象不是固定的,也就是说,装饰类可以根据在调用时传入的参数,装饰任意一个实现了 Component 接口的类。

二、动态代理

动态代理的根据实现方式的不同可以分为 JDK 动态代理和 CGlib 动态代理。

JDK 动态代理:利用反射机制生成一个实现代理接口的类,在调用具体方法前调用InvokeHandler来处理。
CGlib 动态代理:利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
区别:JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

JDK 动态代理

还是以上面的例子为例:
首先,定一个类实现 InvocationHandler 接口,并实现 invoke 方法:

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;
    }
}

然后通过 Proxy.newProxyInstance()

Différences par rapport au motif décorateur

En fait, il existe de nombreuses différences entre le motif décorateur et le motif proxy. Le modèle Decorator se concentre sur l'ajout dynamique de méthodes sur un objet, tandis que le modèle Proxy se concentre sur le contrôle de l'accès à l'objet. En d’autres termes, en utilisant le modèle proxy, une classe proxy peut cacher les informations spécifiques d’un objet à ses clients. Par conséquent, lorsque nous utilisons le modèle proxy, nous créons souvent une instance d’un objet dans une classe proxy. Et, lorsque nous utilisons le modèle décorateur, notre approche habituelle consiste à transmettre l'objet d'origine en tant que paramètre au constructeur du décorateur.

Nous pouvons résumer ces différences dans une autre phrase : en utilisant le modèle proxy, la relation entre le proxy et l'objet réel est généralement déterminée au moment de la compilation, tandis que le décorateur peut être construit de manière récursive au moment de l'exécution. Exemple danalyse du proxy dynamique Java et du proxy statique

Premier regard sur la différence entre les diagrammes de classes UML des deux :

Mode agent :

Analyse des instances de proxy dynamique Java et de proxy statique

Mode Décorateur :

Analyse des instances de proxy dynamique Java et de proxy statiqueExemple danalyse du proxy dynamique Java et du proxy statique

Les deux pseudocodes : 🎜🎜🎜Mode agent : 🎜🎜
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");
    }
}
🎜🎜Mode décorateur ; 🎜🎜
package com.sharpcj.proxytest;
public interface IWork {
    IWork work(String subject);
}
🎜En fait, proxy mode Différent de l'objectif du modèle de décorateur, l'objectif du modèle de proxy est de clarifier la classe proxy. Comme dans l’exemple ci-dessus, le secrétaire veut clairement agir pour le leader. Le modèle décorateur se concentre sur les méthodes d'extension des classes.Les objets des classes qui implémentent l'interface Component détenues par la classe décorée ne sont pas fixes, c'est-à-dire que la classe décorée peut décorer n'importe quel objet qui implémente l'interface Component en fonction des paramètres passés. pendant le cours par téléphone. 🎜🎜2. Proxy dynamique🎜🎜Le proxy dynamique peut être divisé en proxy dynamique JDK et proxy dynamique CGlib en fonction de différentes méthodes d'implémentation. 🎜🎜🎜Procuration dynamique JDK : 🎜Utilisez le mécanisme de réflexion pour générer une classe qui implémente l'interface proxy et appelez InvokeHandler pour la traiter avant d'appeler la méthode spécifique.
🎜Proxy dynamique CGlib :🎜Utilisez le package open source ASM (bibliothèque d'édition de bytecode Java open source, bytecode d'exploitation) pour charger le fichier de classe de la classe d'objet proxy et générer des sous-classes en modifiant son bytecode.
🎜Différence :🎜Le proxy JDK ne peut générer des proxys que pour les classes qui implémentent des interfaces ; CGlib implémente des proxys pour les classes, génère une sous-classe pour la classe spécifiée et remplace les méthodes qu'elle contient. ne peut pas proxy les classes finales modifiées. 🎜🎜Proxy dynamique JDK🎜🎜🎜Prenons l'exemple ci-dessus comme exemple :🎜
Tout d'abord, définissez une classe pour implémenter l'interface InvocationHandler et implémentez la méthode d'invocation : 🎜
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;
    }
}
🎜Puis passez La méthode Proxy.newProxyInstance() crée un objet proxy : 🎜
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");
    }
}
🎜🎜Résultat de sortie : 🎜🎜🎜🎜🎜🎜Nous voyons qu'à travers la classe WorkInvocationHandler, nous pouvons également proxy l'implémentation de la méthode de la classe Leader . En fait, nous implémentons C'est l'implémentation de n'importe quelle méthode, mais lorsque nous créons l'objet proxy, nous transmettons l'interface Iwork et l'objet de classe Leader. 🎜🎜🎜🎜Ce qu'il faut noter ici est : 🎜Le premier paramètre proxy dans la méthode d'invocation de l'interface InvocationHandler n'est pas l'objet de notre méthode appelante. Alors, quel est ce paramètre ? Dans le code, j'ai spécialement ajouté l'impression correspondante pour imprimer le nom de classe du proxy. En fait, proxy est l'objet proxy lui-même. Cela signifie que nous pouvons renvoyer l'objet proxy dans la méthode d'invocation, puis effectuer des appels continus. 🎜🎜🎜Regardez l'exemple suivant : 🎜
... 
dependencies {
    // 引入 cglib 库
    compile 'cglib:cglib:3.1'
    testCompile group: 'junit', name: 'junit', version: '4.12'
}
...
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");
    }
}
🎜🎜Les résultats sont les suivants : 🎜🎜🎜🎜🎜

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");
    }
}

结果如下:

Exemple danalyse du proxy dynamique Java et du proxy statique

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

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

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

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

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

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

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer