web-dev-qa-db-fra.com

Que sont les classes Dynamic Proxy et pourquoi en utiliserais-je une?

Qu'est-ce qu'un cas d'utilisation pour utiliser un proxy dynamique?

Comment sont-ils liés à la génération et à la réflexion de bytecode?

Une lecture recommandée?

67
cwash

Je recommande fortement cette ressource .

Tout d'abord, vous devez comprendre le cas d'utilisation du modèle de proxy. N'oubliez pas que l'objectif principal d'un proxy est de contrôler l'accès à l'objet cible, plutôt que d'améliorer les fonctionnalités de l'objet cible. Le contrôle d'accès comprend la synchronisation, l'authentification, l'accès à distance (RPC), l'instanciation paresseuse (Hibernate, Mybatis), AOP (transaction).

Contrairement au proxy statique, le proxy dynamique génère un bytecode qui nécessite une réflexion Java lors de l'exécution. Avec l'approche dynamique, vous n'avez pas besoin de créer la classe proxy, ce qui peut conduire à plus de commodité.

25
xiaojieaa

Une classe de proxy dynamique est une classe qui implémente une liste d'interfaces spécifiées au moment de l'exécution de telle sorte qu'une invocation de méthode via l'une des interfaces sur une instance de la classe sera encodé et envoyé à un autre objet via une interface uniforme. Il peut être utilisé pour créer un objet proxy de type sécurisé pour une liste d'interfaces sans nécessiter de pré-génération de la classe proxy. Les classes de proxy dynamiques sont utiles pour une application ou une bibliothèque qui doit fournir une répartition réfléchie de type sûre des invocations sur les objets qui présentent des API d'interface.

Classes de proxy dynamique

Je viens de trouver une utilisation intéressante pour un proxy dynamique.

Nous rencontrions quelques difficultés avec un service non critique couplé à un autre service dépendant et nous voulions explorer des moyens d'être tolérant aux pannes lorsque ce service dépendant devenait indisponible.

J'ai donc écrit un LoadSheddingProxy qui prend deux délégués - l'un est l'implémentation distante pour le service "normal" (après la recherche JNDI). L'autre objet est un outil de délestage "factice". Il existe une logique simple entourant chaque méthode invoquée qui intercepte les délais d'expiration et dévie vers le mannequin pendant un certain temps avant de réessayer. Voici comment je l'utilise:

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

Et voici le 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

La classe Java.lang.reflect.Proxy vous permet d'implémenter des interfaces dynamiquement en gérant les appels de méthode dans un InvocationHandler . Il est considéré comme faisant partie de la fonction de réflexion de Java, mais n'a rien à voir avec la génération de bytecode.

Sun a n tutoriel sur l'utilisation de la classe Proxy. Google aide aussi.

8

Un cas d'utilisation est la mise en veille prolongée - il vous donne des objets implémentant votre interface de classes de modèle mais sous les getters et les setters, il y a du code lié à db. C'est à dire. vous les utilisez comme s'ils n'étaient que de simples POJO, mais en réalité, il se passe beaucoup de choses sous couvert.

Par exemple - vous appelez simplement un getter de propriété chargée paresseusement, mais en réalité la propriété (probablement toute la structure d'un grand objet) est récupérée dans la base de données.

Vous devriez vérifier cglib bibliothèque pour plus d'informations.

5
miceuz