Heim  >  Artikel  >  Java  >  Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

不言
不言nach vorne
2018-10-22 14:53:532821Durchsuche

Dieser Artikel bietet Ihnen eine Einführung in die vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys. Ich hoffe, dass er für Sie hilfreich ist.

Interviewfrage: Wie viele Implementierungsmethoden gibt es für das Proxy Design Pattern in Java? Diese Frage ist der Frage von Kong Yiji sehr ähnlich: „Wie schreibt man das Wort Fenchel für Fenchelbohnen?“

Der sogenannte Proxy-Modus bedeutet, dass der Client das eigentliche Objekt (das eine) nicht direkt aufruft in der unteren rechten Ecke des Bildes unten) RealSubject), ruft aber indirekt das eigentliche Objekt auf, indem es den Proxy aufruft.

Der Proxy-Modus wird im Allgemeinen verwendet, weil der Client nicht direkt auf das eigentliche Objekt zugreifen möchte oder es technische Hindernisse beim Zugriff auf das eigentliche Objekt gibt, sodass das Proxy-Objekt als Brücke für den vollständigen indirekten Zugriff verwendet wird.

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

Implementierungsmethode eins: statischer Proxy

Entwickeln Sie eine Schnittstelle IDeveloper, die eine Methode writeCode enthält, Code schreiben.

public interface IDeveloper {

     public void writeCode();

}

Erstellen Sie eine Developer-Klasse, um diese Schnittstelle zu implementieren.

public class Developer implements IDeveloper{
    private String name;
    public Developer(String name){
        this.name = name;
    }
    @Override
    public void writeCode() {
        System.out.println("Developer " + name + " writes code");
    }
}

Code testen: Erstellen Sie eine Entwicklerinstanz namens Jerry und schreiben Sie Code!

public class DeveloperTest {
    public static void main(String[] args) {
        IDeveloper jerry = new Developer("Jerry");
        jerry.writeCode();
    }
}

Jetzt kommt das Problem. Jerrys Projektmanager war sehr unzufrieden, dass Jerry nur Code schrieb, ohne jegliche Dokumentation zu führen. Angenommen, Jerry fährt eines Tages in den Urlaub und andere Programmierer übernehmen Jerrys Arbeit und betrachten den unbekannten Code mit Fragezeichen im Gesicht. Nach einer Diskussion in der gesamten Gruppe wurde beschlossen, dass die Dokumentation gleichzeitig aktualisiert werden muss, wenn jeder Entwickler Code schreibt.

Um jeden Programmierer dazu zu zwingen, beim Entwickeln daran zu denken, Dokumente zu schreiben, ohne die Aktion des Codeschreibens selbst zu beeinträchtigen, ändern wir nicht die ursprüngliche Developer-Klasse, sondern erstellen eine neue Klasse und implementieren dieselbe IDeveloper-Schnittstelle. Diese neue Klasse DeveloperProxy verwaltet intern eine Mitgliedsvariable, die auf die ursprüngliche IDeveloper-Instanz verweist:

public class DeveloperProxy implements IDeveloper{
    private IDeveloper developer;
    public DeveloperProxy(IDeveloper developer){
        this.developer = developer;
    }
    @Override
    public void writeCode() {
        System.out.println("Write documentation...");
        this.developer.writeCode();
    }
}

In der von dieser Proxy-Klasse implementierten writeCode-Methode wird vor dem Aufruf der writeCode-Methode des eigentlichen Programmierers ein Aufruf zum Schreiben des Dokuments hinzugefügt Dadurch wird sichergestellt, dass Programmierer beim Schreiben von Code eine aktualisierte Dokumentation erhalten.

Testcode:

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

Vorteile der statischen Proxy-Methode

Einfach Verständnis und Implementierung

2. Die Beziehung zwischen der Proxy-Klasse und der realen Klasse wird während der Kompilierung statisch bestimmt. Im Vergleich zum unmittelbar unten vorgestellten dynamischen Proxy entsteht kein zusätzlicher Overhead bei der Ausführung.

Nachteile der statischen Proxy-Methode

Für jede echte Klasse muss eine neue Proxy-Klasse erstellt werden. Nehmen wir als Beispiel die obige Dokumentenaktualisierung: Angenommen, der Chef stellt auch neue Anforderungen an Testingenieure und fordert die Testingenieure auf, die entsprechenden Testdokumente jedes Mal, wenn sie einen Fehler entdecken, rechtzeitig zu aktualisieren. Anschließend muss die Implementierungsklasse ITester des Testingenieurs mithilfe der statischen Proxy-Methode auch eine entsprechende ITesterProxy-Klasse erstellen.

public interface ITester {
    public void doTesting();
}
Original tester implementation class:
public class Tester implements ITester {
    private String name;
    public Tester(String name){
        this.name = name;
    }
    @Override
    public void doTesting() {
        System.out.println("Tester " + name + " is testing code");
    }
}
public class TesterProxy implements ITester{
    private ITester tester;
    public TesterProxy(ITester tester){
        this.tester = tester;
    }
    @Override
    public void doTesting() {
        System.out.println("Tester is preparing test documentation...");
        tester.doTesting();
    }
}

Genau aufgrund dieses Mangels der statischen Codemethode wurde die dynamische Proxy-Implementierungsmethode von Java geboren.

Java Dynamic Proxy-Implementierungsmethode eins: InvocationHandler

Ich habe einen Artikel speziell geschrieben, um das Prinzip von InvocationHandler vorzustellen: Das einfachste Einführungs-Tutorial für Java Dynamic Proxy InvocationHandler

Über InvocationHandler kann ich eine EnginnerProxy-Proxy-Klasse verwenden, um das Verhalten von Entwickler und Tester gleichzeitig zu vertreten.

public class EnginnerProxy implements InvocationHandler {
    Object obj;
    public Object bind(Object obj)
    {
        this.obj = obj;
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj
        .getClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
    throws Throwable
    {
        System.out.println("Enginner writes document");
        Object res = method.invoke(obj, args);
        return res;
    }
}

Die Methoden writeCode und doTesting der realen Klasse werden durch Reflektion in der dynamischen Proxy-Klasse ausgeführt.

Testausgabe:

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

Einschränkungen der Implementierung eines dynamischen Proxys durch InvocationHandler

Angenommen, es gibt eine Produktmanagerklasse (ProductOwner) implementiert keine Schnittstelle.

public class ProductOwner {
    private String name;
    public ProductOwner(String name){
        this.name = name;
    }
    public void defineBackLog(){
        System.out.println("PO: " + name + " defines Backlog.");
    }
}

Wir verwenden weiterhin die Proxy-Klasse EnginnerProxy als Proxy, und beim Kompilieren treten keine Fehler auf. Was passiert zur Laufzeit?

ProductOwner po = new ProductOwner("Ross");

ProductOwner poProxy = (ProductOwner) new EnginnerProxy().bind(po);

poProxy.defineBackLog();

Beim Ausführen ist ein Fehler aufgetreten. Die Einschränkung besteht also darin: Wenn die Proxy-Klasse keine Schnittstelle implementiert, können Sie ihr Verhalten nicht über den dynamischen InvocationHandler-Proxy vertreten.

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

Java Dynamic Proxy-Implementierungsmethode zwei: CGLIB

CGLIB ist eine Java-Bytecode-Generierungsbibliothek, die bereitgestellt wird eine benutzerfreundliche API zum Erstellen und Ändern von Java-Bytecode. Weitere Informationen zu dieser Open-Source-Bibliothek finden Sie im CGLIB-Repository auf Github: https://github.com/cglib/cglib

Wir versuchen jetzt, CGLIB als Proxy zu verwenden, bevor wir InvocationHandler verwenden, ohne Erfolg Klasse (diese Klasse implementiert keine Schnittstelle).

Jetzt verwende ich stattdessen die CGLIB-API, um die Proxy-Klasse zu erstellen:

public class EnginnerCGLibProxy {
    Object obj;
    public Object bind(final Object target)
    {
        this.obj = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable
            {
                System.out.println("Enginner 2 writes document");
                Object res = method.invoke(target, args);
                return res;
            }
        }
        );
        return enhancer.create();
    }
}

Testcode:

ProductOwner ross = new ProductOwner("Ross");

ProductOwner rossProxy = (ProductOwner) new EnginnerCGLibProxy().bind(ross);

rossProxy.defineBackLog();

尽管ProductOwner未实现任何代码,但它也成功被代理了:

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

用CGLIB实现Java动态代理的局限性

如果我们了解了CGLIB创建代理类的原理,那么其局限性也就一目了然。我们现在做个实验,将ProductOwner类加上final修饰符,使其不可被继承:

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

再次执行测试代码,这次就报错了: Cannot subclass final class XXXX。

所以通过CGLIB成功创建的动态代理,实际是被代理类的一个子类。那么如果被代理类被标记成final,也就无法通过CGLIB去创建动态代理。

Java动态代理实现方式三:通过编译期提供的API动态创建代理类

假设我们确实需要给一个既是final,又未实现任何接口的ProductOwner类创建动态代码。除了InvocationHandler和CGLIB外,我们还有最后一招:

我直接把一个代理类的源代码用字符串拼出来,然后基于这个字符串调用JDK的Compiler(编译期)API,动态的创建一个新的.java文件,然后动态编译这个.java文件,这样也能得到一个新的代理类。

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

测试成功:

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

我拼好了代码类的源代码,动态创建了代理类的.java文件,能够在Eclipse里打开这个用代码创建的.java文件,

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

下图是如何动态创建ProductPwnerSCProxy.java文件:

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

下图是如何用JavaCompiler API动态编译前一步动态创建出的.java文件,生成.class文件:

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

下图是如何用类加载器加载编译好的.class文件到内存:

Einführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java

如果您想试试这篇文章介绍的这四种代理模式(Proxy Design Pattern), 请参考我的github仓库,全部代码都在上面。感谢阅读。

https://github.com/i042416/Ja...

Das obige ist der detaillierte Inhalt vonEinführung in vier Implementierungsmethoden des statischen Proxys und des dynamischen Proxys in Java. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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