it-swarm.com.de

Was sind Dynamic Proxy-Klassen und warum sollte ich eine verwenden?

Was ist ein Anwendungsfall für die Verwendung eines dynamischen Proxys?

Wie hängen sie mit der Erzeugung und Reflektion von Bytecodes zusammen?

Jede empfohlene Lektüre?

67
cwash

Ich empfehle diese Ressource .

Zunächst müssen Sie den Anwendungsfall des Proxy-Musters verstehen. Denken Sie daran, dass die Hauptabsicht eines Proxys darin besteht, den Zugriff auf das Zielobjekt zu steuern, anstatt die Funktionalität des Zielobjekts zu verbessern. Die Zugriffskontrolle umfasst Synchronisierung, Authentifizierung, Remotezugriff (RPC), verzögerte Instanziierung (Ruhezustand, Mybatis) und AOP (Transaktion).

Im Gegensatz zum statischen Proxy generiert der dynamische Proxy einen Bytecode, der zur Laufzeit eine Java Reflektion erfordert. Mit dem dynamischen Ansatz müssen Sie keine Proxy-Klasse erstellen, was zu mehr Komfort führen kann.

25
xiaojieaa

Eine dynamische Proxy-Klasse ist eine Klasse, die eine Liste von zur Laufzeit angegebenen Schnittstellen implementiert, sodass ein Methodenaufruf über eine der Schnittstellen in einer Instanz der Klasse erfolgt werden verschlüsselt und über eine einheitliche Schnittstelle an ein anderes Objekt gesendet. Es kann verwendet werden, um ein typsicheres Proxy-Objekt für eine Liste von Schnittstellen zu erstellen, ohne dass die Proxy-Klasse vorab generiert werden muss. Dynamische Proxy-Klassen sind nützlich für eine Anwendung oder Bibliothek, die einen typsicheren reflektierenden Versand von Aufrufen für Objekte bereitstellen muss, die Schnittstellen-APIs darstellen.

Dynamische Proxy-Klassen

Ich habe gerade eine interessante Verwendung für einen dynamischen Proxy gefunden.

Wir hatten einige Probleme mit einem nicht kritischen Dienst, der mit einem anderen abhängigen Dienst gekoppelt ist, und wollten Möglichkeiten ausloten, fehlertolerant zu sein, wenn dieser abhängige Dienst nicht mehr verfügbar ist.

Also schrieb ich ein LoadSheddingProxy, das zwei Delegierte benötigt - eines ist das Remote-Impl für den 'normalen' Service (nach dem JNDI-Lookup). Das andere Objekt ist ein 'Dummy'-Lastabwurfgerät. Jeder Methodenaufruf ist von einer einfachen Logik umgeben, die Zeitüberschreitungen abfängt und vor einem erneuten Versuch für eine bestimmte Zeitspanne zum Dummy umleitet. So benutze ich es:

// This is part of your ServiceLocator class
public static MyServiceInterface getMyService() throws Exception
{
    MyServiceInterface loadShedder = new MyServiceInterface() {
        public Thingy[] getThingys(Stuff[] whatever) throws Exception {
            return new Thingy[0];
        }
        //... etc - basically a dummy version of your service goes here
    }           
    Context ctx = JndiUtil.getJNDIContext(MY_CLUSTER);
    try {
        MyServiceInterface impl = ((MyServiceHome) PortableRemoteObject.narrow(
                ctx.lookup(MyServiceHome.JNDI_NAME), 
                MyServiceHome.class)).create();
        // Here's where the proxy comes in
        return (MyService) Proxy.newProxyInstance(
            MyServiceHome.class.getClassLoader(),
        new Class[] { MyServiceInterface.class },
        new LoadSheddingProxy(MyServiceHome.JNDI_NAME, impl, loadShedder, 60000));  // 10 minute retry
    } catch (RemoteException e) {    // If we can't even look up the service we can fail by shedding load too
        logger.warn("Shedding load");
        return loadShedder;
    } finally {
        if (ctx != null) {
        ctx.close();
        }
    }
}

Und hier ist der Proxy:

public class LoadSheddingProxy implements InvocationHandler {

static final Logger logger = ApplicationLogger.getLogger(LoadSheddingProxy.class);

Object primaryImpl, loadDumpingImpl;
long retry;
String serviceName;
// map is static because we may have many instances of a proxy around repeatedly looked-up remote objects
static final Map<String, Long> servicesLastTimedOut = new HashMap<String, Long>();

public LoadSheddingProxy(String serviceName, Object primaryImpl, Object loadDumpingImpl, long retry)
{
    this.serviceName = serviceName;
    this.primaryImpl = primaryImpl;
    this.loadDumpingImpl = loadDumpingImpl;
    this.retry = retry;
}

public Object invoke(Object obj, Method m, Object[] args) throws Throwable
{
    try
    {
        if (!servicesLastTimedOut.containsKey(serviceName) || timeToRetry()) {
            Object ret = m.invoke(primaryImpl, args);
            servicesLastTimedOut.remove(serviceName);
            return ret;
        } 
        return m.invoke(loadDumpingImpl, args);
    }
    catch (InvocationTargetException e)
    {
        Throwable targetException = e.getTargetException();

        // DETECT TIMEOUT HERE SOMEHOW - not sure this is the way to do it???
        if (targetException instanceof RemoteException) {
            servicesLastTimedOut.put(serviceName, Long.valueOf(System.currentTimeMillis()));
        }
        throw targetException;
    }                    
}

private boolean timeToRetry() {
    long lastFailedAt = servicesLastTimedOut.get(serviceName).longValue();
    return (System.currentTimeMillis() - lastFailedAt) > retry;
}
}
16
Jim P

Die Klasse Java.lang.reflect.Proxy ermöglicht es Ihnen, Schnittstellen dynamisch zu implementieren, indem Sie Methodenaufrufe in einem InvocationHandler verarbeiten. Es wird als Teil der Java-Reflektionsfunktion betrachtet, hat jedoch nichts mit der Erzeugung von Bytecode zu tun.

Sun hat ein Tutorial über die Verwendung der Proxy-Klasse. Google hilft auch.

8

Ein Anwendungsfall ist der Ruhezustand - es gibt Ihnen Objekte, die Ihre Modellklassen-Schnittstelle implementieren, aber unter Gettern und Setzern befindet sich db-bezogener Code. Das heißt Sie benutzen sie, als wären sie nur ein einfaches POJO, aber im Verborgenen ist tatsächlich viel los.

Zum Beispiel - Sie rufen nur einen Getter einer träge geladenen Eigenschaft auf, aber tatsächlich wird die Eigenschaft (wahrscheinlich die gesamte große Objektstruktur) aus der Datenbank abgerufen.

Sie sollten cglib Bibliothek für weitere Informationen überprüfen.

5
miceuz