Heim  >  Artikel  >  Java  >  Das Prinzip des dynamischen Java-Proxys

Das Prinzip des dynamischen Java-Proxys

尚
nach vorne
2019-11-29 17:06:402020Durchsuche

Das Prinzip des dynamischen Java-Proxys

Das Aufkommen des dynamischen Java-Proxy-Mechanismus ermöglicht es Java-Entwicklern, Proxy-Klassen dynamisch zu erhalten, ohne Proxy-Klassen manuell schreiben zu müssen. Sie können einfach eine Reihe von Schnittstellen angeben und Klassenobjekte delegieren. (Empfohlen: Java-Video-Tutorial)

Die Proxy-Klasse ist für die Weiterleitung aller Methodenaufrufe an das Delegate-Objekt zur Reflexionsausführung verantwortlich. Während des Versandausführungsprozesses können Entwickler bei Bedarf auch Anpassungen vornehmen Delegieren Sie Klassenobjekte und ihre Funktionen. Dies ist ein sehr flexibles und flexibles Proxy-Framework. Als nächstes lernen wir dynamische Agenten kennen.

Eine kurze Beschreibung des dynamischen Proxys

Im dynamischen Proxy-Mechanismus von Java gibt es zwei wichtige Klassen oder Schnittstellen, eine davon ist InvocationHandler( Schnittstelle), das andere ist Proxy (Klasse).

1. Beschreibung des InvocationHandler (Schnittstelle):

InvocationHandler is the interface implemented by the invocation handler of a proxy instance. 

Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.

Jede dynamische Proxy-Klasse muss die InvocationHandler-Schnittstelle implementieren, und jede Proxy-Klasseninstanz ist zugeordnet, wenn wir einen Handler erreichen Wenn wir eine Methode über das Proxy-Objekt aufrufen, wird der Aufruf dieser Methode weitergeleitet, um von der Aufrufmethode der InvocationHandler-Schnittstelle aufgerufen zu werden. Werfen wir einen Blick auf die einzige Methode der InvocationHandler-Schnittstelle, die Aufrufmethode:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

Diese Methode empfängt drei Parameter und gibt einen Objekttyp zurück. Ihre jeweiligen Bedeutungen sind wie folgt:

proxy: bezieht sich auf das reale Objekt, das wir darstellen

Methode: bezieht sich auf das Methodenobjekt, das wir als Methode des realen Objekts aufrufen möchten

args: bezieht sich auf die Parameter, die beim Aufruf einer Methode des realen Objekts empfangen werden Objekt Das vom Parameter

zurückgegebene Objekt bezieht sich auf den Rückgabetyp der realen Objektmethode. Das Obige wird in den folgenden Beispielen ausführlicher verstanden.

the value to return from the method invocation on the proxy instance.

2. Beschreibung des Proxys (Klasse):

Proxy stellt statische Methoden zum Erstellen dynamischer Proxy-Klassen und -Instanzen bereit und ist auch die Oberklasse aller von diesen Methoden erstellten dynamischen Proxy-Klassen.

Die Funktion der Proxy-Klasse besteht darin, dynamisch ein Proxy-Objekt zu erstellen. Wir verwenden häufig die newProxyInstance-Methode:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException

Verständnis der Parameter:

// 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
loader - the class loader to define the proxy class  
// 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口
interfaces - the list of interfaces for the proxy class to implement 
// 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
h - the invocation handler to dispatch method invocations to

Verständnis der Rückgabeergebnisse: Eine Instanz eines Proxy-Objekts

eine Proxy-Instanz mit dem angegebenen Aufrufhandler einer Proxy-Klasse, die durch den angegebenen Klassenlader definiert wird und die angegebenen Schnittstellen implementiert

Einfacher Java-Proxy

Wir erstellen ein Java-Projekt zum Testen und Verständnis des dynamischen Proxys. Die Projektstruktur ist wie folgt:

Das Prinzip des dynamischen Java-Proxys

1. Definieren Sie zunächst eine Schnittstelle und fügen Sie zwei Methoden hinzu.

package com.huhx.proxy;

public interface Interface {
    void getMyName();

    String getNameById(String id);
}

2. Definieren Sie eine echte Klasse, die die obige Schnittstelle implementiert, RealObject:

package com.huhx.proxy;

public class RealObject implements Interface {
    @Override
    public void getMyName() {
        System.out.println("my name is huhx");
    }

    @Override
    public String getNameById(String id) {
        System.out.println("argument id: " + id);
        return "huhx";
    }
}

3. Definieren Sie ein Proxy-Objekt, das auch ist implementiert Die oben genannte Interface-Schnittstelle:

package com.huhx.proxy;

public class SimpleProxy implements Interface {
    private Interface proxied;

    public SimpleProxy(Interface proxied) {
        this.proxied = proxied;
    }

    @Override
    public void getMyName() {
        System.out.println("proxy getmyname");
        proxied.getMyName();
    }

    @Override
    public String getNameById(String id) {
        System.out.println("proxy getnamebyid");
        return proxied.getNameById(id);
    }
}

4. SimpleMain testet die oben genannten Ergebnisse in der Main-Methode:

package com.huhx.proxy;

public class SimpleMain {
    private static void consume(Interface iface) {
        iface.getMyName();
        String name = iface.getNameById("1");
        System.out.println("name: " + name);
    }

    public static void main(String[] args) {
        consume(new RealObject());
        System.out.println("========================================================");
        consume(new SimpleProxy(new RealObject()));
    }
}

5. Das Ausführen Die Ergebnisse sind wie folgt:

my name is huhx
argument id: 1
name: huhx
========================================================
proxy getmyname
my name is huhx
proxy getnamebyid
argument id: 1
name: huhx

Dynamischer Java-Proxy

Nachdem wir den oben genannten einfachen Java-Proxy fertiggestellt haben, beginnen wir nun, Java zu lernen Dynamischer Proxy. Es geht einen Schritt weiter als die Idee von Proxys, da es Proxys dynamisch erstellen und Aufrufe der Proxy-Methoden dynamisch verarbeiten kann. Alle über den dynamischen Proxy getätigten Anrufe werden an einen einzelnen Anrufabwickler umgeleitet, dessen Aufgabe es ist, die Art des Anrufs offenzulegen und die geeignete Gegenmaßnahme zu ermitteln. Im Folgenden verwenden wir Fälle, um unser Verständnis der dynamischen Java-Proxys zu vertiefen:

1. Erstellen Sie einen Prozessor, der InvocationHandler erbt: DynamicProxyHandler

package com.huhx.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Arrays;

public class DynamicProxyHandler implements InvocationHandler {
    private Object proxied;

    public DynamicProxyHandler(Object proxied) {
        System.out.println("dynamic proxy handler constuctor: " + proxied.getClass());
        this.proxied = proxied;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("dynamic proxy name: " + proxy.getClass());
        System.out.println("method: " + method.getName());
        System.out.println("args: " + Arrays.toString(args));
        
        Object invokeObject = method.invoke(proxied, args);
        if (invokeObject != null) {
            System.out.println("invoke object: " + invokeObject.getClass());
        } else {
            System.out.println("invoke object is null");
        }
        return invokeObject;
    }
}

2. Wir schreiben A test Main-Methode, DynamicProxyMain:

package com.huhx.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.huhx.proxy.Interface;
import com.huhx.proxy.RealObject;

public class DynamicProxyMain {
    public static void consumer(Interface iface) {
        iface.getMyName();
        String name = iface.getNameById("1");
        System.out.println("name: " + name);
    }

    public static void main(String[] args) throws Exception, SecurityException, Throwable {
        RealObject realObject = new RealObject();
        consumer(realObject);
        System.out.println("==============================");

        // 动态代理
        ClassLoader classLoader = Interface.class.getClassLoader();
        Class<?>[] interfaces = new Class[] { Interface.class };
        InvocationHandler handler = new DynamicProxyHandler(realObject);
        Interface proxy = (Interface) Proxy.newProxyInstance(classLoader, interfaces, handler);

        System.out.println("in dynamicproxyMain proxy: " + proxy.getClass());
        consumer(proxy);
    }
}

3. Die laufenden Ergebnisse sind wie folgt:

my name is huhx
argument id: 1
name: huhx
==============================
dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
in dynamicproxyMain proxy: class com.sun.proxy.$Proxy0
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getNameById
args: [1]
argument id: 1
invoke object: class java.lang.String
name: huhx

Aus den obigen Ausgabeergebnissen können wir die folgenden Schlussfolgerungen ziehen :

Der mit dem Proxy-Objekt verknüpfte InvocationHandler führt seine Aufrufmethode nur aus, wenn das Proxy-Objekt eine Methode aufruft

Verständnis der drei Parameter von invoke: Objekt-Proxy ist das Objekt des Proxys, Die Methodenmethode ist die Methodenklasse, die die Methode im realen Objekt aufruft. Object[] args sind die Parameter, die die Methode im realen Objekt aufrufen

Das Prinzip des dynamischen Java-Proxys

1. Der Schlüsselcode des dynamischen Proxys ist Proxy.newProxyInstance(classLoader, interfaces, handler) und werfen wir einen Blick darauf:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
  // handler不能为空
    if (h == null) {
        throw new NullPointerException();
    }

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     */
  // 通过loader和接口,得到代理的Class对象
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
            // create proxy instance with doPrivilege as the proxy class may
            // implement non-public interfaces that requires a special permission
            return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    return newInstance(cons, ih);
                }
            });
        } else {
       // 创建代理对象的实例
            return newInstance(cons, ih);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    }
}

2. Werfen wir einen Blick auf den Quellcode der newInstance-Methode:

private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
    try {
        return cons.newInstance(new Object[] {h} );
    } catch (IllegalAccessException | InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString());
        }
    }
}

3. Wenn wir eine Methode über das Proxy-Objekt aufrufen, erfolgt der Aufruf dieser Methode an die invoke-Methode des InvocationHandler-Schnittstellenaufrufs weitergeleitet.

Ich konnte den Code, der diesen Satz verkörpert, im Quellcode nicht finden, daher habe ich den folgenden Code zur Hauptmethode der Testklasse hinzugefügt:

if (proxy instanceof Proxy) {
    InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
    invocationHandler.invoke(proxy, realObject.getClass().getMethod("getMyName"), null);
    System.out.println("--------------------------------------");
}

这段代码的输出结果如下,与上述中调用代理对象中的getMyName方法输出是一样的,不知道Jvm底层是否是这样判断的:

dynamic proxy handler constuctor: class com.huhx.proxy.RealObject
dynamic proxy name: class com.sun.proxy.$Proxy0
method: getMyName
args: null
my name is huhx
invoke object is null
--------------------------------------

更多java知识请关注java基础教程栏目。

Das obige ist der detaillierte Inhalt vonDas Prinzip des dynamischen Java-Proxys. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

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