Heim  >  Artikel  >  Java  >  Beispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys

Beispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys

PHPz
PHPznach vorne
2023-05-06 23:46:06929Durchsuche

1. Statischer Proxy

Verwendung eines statischen Proxys

Statischer Proxy, die Proxy-Klasse und die Proxy-Klasse implementieren dieselbe Schnittstelle, und die Proxy-Klasse enthält auch die Proxy-Klasse. Ein Verweis auf die Proxy-Klasse. Wenn wir die Methode der Proxy-Klasse aufrufen müssen, können wir dies durch Aufrufen der Methode der Proxy-Klasse tun.

Beispiel: Gehen Sie davon aus, dass die Aufgabe der Führungskraft darin besteht, Besprechungen abzuhalten und Mitarbeiter zu bewerten.

Definieren Sie zuerst eine Schnittstelle:

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

Dann definieren Sie die Leader-Klasse:

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;
    }
}
# ?? Ergebnis: # 🎜🎜#

Dieser Code ist sehr einfach, wenn wir die Besprechungsmethode der Klasse Sekretär aufrufen Rufen Sie die Meeting-Methode der Leader-Klasse auf. Zuvor haben wir auch diese Methode erweitert. An diesem Punkt könnten einige Leute verwirrt sein. Dies scheint ein bisschen wie ein Dekorationsmuster zu sein. Was ist los? Der Unterschied zwischen dem Dekoratormuster und dem Dekoratormuster

Tatsächlich gibt es immer noch viele Unterschiede zwischen dem Dekoratormuster und dem Proxy-Muster. Das Decorator-Muster konzentriert sich auf das dynamische Hinzufügen von Methoden zu einem Objekt, während sich das Proxy-Muster auf die Steuerung des Zugriffs auf das Objekt konzentriert. Mit anderen Worten: Mithilfe des Proxy-Musters kann eine Proxy-Klasse die spezifischen Informationen eines Objekts vor ihren Clients verbergen. Daher erstellen wir bei Verwendung des Proxy-Musters häufig eine Instanz eines Objekts in einer Proxy-Klasse. Und wenn wir das Dekoratormuster verwenden, besteht unser üblicher Ansatz darin, das Originalobjekt als Parameter an den Konstruktor des Dekorators zu übergeben.

Wir können diese Unterschiede in einem anderen Satz zusammenfassen: Mithilfe des Proxy-Musters wird die Beziehung zwischen dem Proxy und dem realen Objekt normalerweise zur Kompilierungszeit bestimmt, während der Dekorateur dies rekursiv konstruieren kann Laufzeit.

Beispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys

Schauen wir uns zunächst den Unterschied zwischen den beiden UML-Klassendiagrammen an: Secretary类的 meeting 方法时,我们调用了Leader类的 meeting 的方法,在此之前,我们还扩充了该方法。这时有的人可能有疑惑了,这看起来有点是装饰者模式了。这到底怎么回事?

与装饰者模式的区别

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

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

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

代理模式:

Beispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys

装饰者模式:

Beispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys

两者伪代码:

代理模式:

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

装饰者模式;

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

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

二、动态代理

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

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

JDK 动态代理

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

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

然后通过 Proxy.newProxyInstance()

Agent-Modus:

Dynamischer Java-Proxy und statischer Proxy InstanzanalyseBeispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys

Decorator-Modus:

Analyse dynamischer Java-Proxys und statischer Proxy-InstanzenPseudocode für beide:

Agentenmodus:

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

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;
    }
}
Beispielanalyse eines dynamischen Java-Proxys und eines statischen ProxysTatsächlich liegen der Proxy-Modus und der Decorator-Modus im Mittelpunkt Anders Der Fokus des Proxy-Modus liegt auf der Angabe der Klasse, die vertreten wird. Wie im obigen Beispiel möchte die Sekretärin eindeutig im Namen des Leiters handeln. Das Dekorationsmuster konzentriert sich auf die Erweiterung der Klassenmethode, die die von der dekorierten Klasse gehaltene Komponentenschnittstelle implementiert. Das heißt, die dekorierte Klasse kann jedes Objekt dekorieren, das die Komponentenschnittstelle implementiert die beim Aufruf der Klasse übergebenen Parameter.

#🎜🎜# 2. Dynamischer Proxy #🎜🎜##🎜🎜#Dynamischer Proxy kann basierend auf unterschiedlichen Implementierungsmethoden in dynamischen JDK-Proxy und dynamischen CGlib-Proxy unterteilt werden. #🎜🎜##🎜🎜##🎜🎜#Dynamischer JDK-Proxy: #🎜🎜#Verwenden Sie den Reflexionsmechanismus, um eine Klasse zu generieren, die die Proxy-Schnittstelle implementiert, und rufen Sie InvokeHandler auf, bevor Sie die spezifische Methode aufrufen.
#🎜🎜#CGlib Dynamischer Proxy: #🎜🎜#Verwenden Sie das Open-Source-Paket ASM (Open-Source-Java-Bytecode-Bearbeitungsbibliothek, Betriebsbytecode), um die Klassendatei der Proxy-Objektklasse zu laden und deren Zeichen zu ändern Abschnittscode generiert zu verarbeitende Unterklassen.
#🎜🎜#Unterschied: #🎜🎜#JDK-Agent kann nur Agenten für Klassen generieren, die Schnittstellen implementieren; CGlib implementiert Agenten für Klassen, generiert eine Unterklasse für die angegebene Klasse und überschreibt die darin enthaltenen Methoden Implementierung geerbter Klassen, endgültig geänderte Klassen können nicht als Proxy verwendet werden. #🎜🎜##🎜🎜#Dynamischer JDK-Proxy#🎜🎜##🎜🎜##🎜🎜# Nehmen Sie weiterhin das obige Beispiel als Beispiel: #🎜🎜#
Definieren Sie zunächst eine Klasse, um zu implementieren InvocationHandler-Schnittstelle und implementieren Sie die Aufrufmethode: #🎜🎜#<pre class="brush:java;">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(&quot;Joy&quot;);*/ Leader leader = new Leader(); IWork proxy = (IWork) Proxy.newProxyInstance(Leader.class.getClassLoader(), new Class[]{IWork.class}, new WorkInvocationHandler(leader)); proxy.meeting(); proxy.evaluate(&quot;Joy&quot;); proxy.evaluate(&quot;James&quot;); } }</pre>#🎜🎜# Erstellen Sie dann ein Proxy-Objekt über die Methode <code>Proxy.newProxyInstance(): #🎜🎜#
package com.sharpcj.proxytest;
public interface IWork {
    IWork work(String subject);
}
#🎜🎜 ##🎜🎜 #Ausgabeergebnis: #🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜##🎜🎜#Wir sehen, dass wir durch die WorkInvocationHandler-Klasse auch die Implementierung der Methoden von vertreten können Tatsächlich implementieren wir eine beliebige Methode, aber wenn wir das Proxy-Objekt erstellen, übergeben wir die Iwork-Schnittstelle und das Leader-Klassenobjekt. #🎜🎜##🎜🎜##🎜🎜##🎜🎜#Hier muss Folgendes beachtet werden: #🎜🎜#Der erste Parameter-Proxy in der Aufrufmethode der InvocationHandler-Schnittstelle ist nicht das Objekt unserer aufrufenden Methode dieser Parameter Was ist das? Im Code habe ich speziell einen entsprechenden Ausdruck hinzugefügt, um den Klassennamen des Proxys auszudrucken. Tatsächlich ist Proxy das Proxy-Objekt selbst. Seine Bedeutung besteht darin, dass wir das Proxy-Objekt in der Aufrufmethode zurückgeben und dann kontinuierlich aufrufen können. #🎜🎜##🎜🎜##🎜🎜#Sehen Sie sich das folgende Beispiel an: #🎜🎜#
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");
    }
}
... 
dependencies {
    // 引入 cglib 库
    compile &#39;cglib:cglib:3.1&#39;
    testCompile group: &#39;junit&#39;, name: &#39;junit&#39;, version: &#39;4.12&#39;
}
...
#🎜🎜##🎜🎜#Das Ergebnis ist wie folgt: #🎜🎜##🎜🎜##🎜🎜## 🎜🎜##🎜 🎜#

CGlib 动态代理实现

首先添加 cglib 依赖

build.gradle 文件:

... 
dependencies {
    // 引入 cglib 库
    compile &#39;cglib:cglib:3.1&#39;
    testCompile group: &#39;junit&#39;, name: &#39;junit&#39;, version: &#39;4.12&#39;
}
...

前面说了,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");
    }
}

结果如下:

Beispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys

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

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

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

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

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

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

Das obige ist der detaillierte Inhalt vonBeispielanalyse eines dynamischen Java-Proxys und eines statischen Proxys. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen