2010-10-05 5 views
3

Comment créer un proxy pour une interface sans créer une classe qui l'implémente?Comment créer un proxy d'une interface en Java?

J'ai un exemple concret: J'ai une interface, Contact, et j'ai besoin de créer un objet proxy qui agit comme un Contact. Cet objet proxy sera utilisé pour exécuter certains tests TestNG.

J'ai essayé d'utiliser l'approche JDK mais je n'ai trouvé que des exemples nécessitant une implémentation réelle de cette interface.

J'ai également trouvé que jasssist peut m'aider dans ce problème et a essayé d'implémenter un exemple simple qui semble fonctionner jusqu'à ce que j'obtienne une erreur de mémoire insuffisante. Voici un extrait de ce que je fais:

import javassist.util.proxy.MethodFilter; 
import javassist.util.proxy.MethodHandler; 
import javassist.util.proxy.ProxyFactory 

protected final T createMock(final Class<T> clazz) { 
    final ProxyFactory factory = new ProxyFactory(); 
    factory.setInterfaces(new Class[] { clazz }); 
    factory.setFilter(new MethodFilter() { 
     public final boolean isHandled(final Method m) { 
      // ignore finalize() 
      return !m.getName().equals("finalize"); 
     } 
    }); 

    final MethodHandler handler = createDefaultMethodHandler(); 
    try { 
     return (T) factory.create(new Class<?>[0], new Object[0], handler); 
    } catch (final Exception e) { 
     e.printStackTrace(); 
    } 
    return null; 
} 
private MethodHandler createDefaultMethodHandler() { 
    return new MethodHandler() { 
     public final Object invoke(final Object self, 
       final Method thisMethod, final Method proceed, 
       final Object[] args) throws Throwable { 
      System.out.println("Handling " + thisMethod 
        + " via the method handler"); 
      return thisMethod.invoke(self, args); 
     } 
    }; 
} 

Rappelez-vous que le paramètre de la méthode createMock() sera une interface.

Merci

+0

Pourquoi ignorer-vous des appels à mettre la dernière main? Peut-être que cela a quelque chose à voir avec vos exceptions OutOfMemory !? – Arne

+0

L'erreur se produit toujours si je prends en considération la méthode finalize(). Je pense que le problème est ailleurs, parce que j'obtiens les mêmes erreurs en utilisant javassist et commons-proxy. Pour le moment, je pense que je fournirai une implémentation simple pour chaque interface qui doit être testée parce que j'ai investi du temps dans ce problème et que je pourrais le faire fonctionner. – virgium03

Répondre

1

Si vous êtes seulement intéressant moqueuse je suggère d'utiliser un cadre.

EasyMock (http://easymock.org/) ou JMock (http://www.jmock.org/) peuvent convenir.

Pour créer vous-même un proxy, vous pouvez utiliser la classe java.lang.reflect.Proxy.

+0

J'ai regardé les frameworks moqueurs et essayé EasyMock mais je voulais juste créer un proxy et ne pas définir des attentes, des comportements et ainsi de suite. – virgium03

1

commons-proxy vise à simplifier la tâche.

Ce que vous voulez est un proxy invoquant (sans objet cible). Vous pouvez donc utiliser:

ProxyFactory factory = new JavassistProxyFactory(); 
Object result = 
     factory.createInvokerProxy(invoker, new Class[] {YourInterface.class}); 

Et votre invoker doit implémenter l'interface Invoker, dont la méthode invoke sera appelée à chaque appel de méthode. (4 fois le mot "invoquer" ici)

Notez que commun-proxy utilise le mécanisme de proxy sous-jacent préféré - dans l'exemple ci-dessus, il est javassist.


Cependant, vous semblez avoir besoin du proxy à des fins de moquerie. Avec mockito c'est aussi facile que:

YourInterface yourMock = mock(YourInterface.class); 
when(yourMock.someMethod()).thenReturn(yourPreferredResult); 
+0

je vais essayer le proxy des communs. Notez que mon extrait fourni fonctionne mais que les tests sont très longs à exécuter, lancent beaucoup d'InvocationTargetExceptions et ensuite OutOfMemory. – virgium03

0

Exampledepot a simple extrait pour savoir comment créer un proxy d'interface basée sur java.lang.reflect.proxy à http://exampledepot.8waytrips.com/egs/java.lang.reflect/ProxyClass.html

Vous avez juste besoin de fournir des fonctionnalités pour le invoke(Object proxy, Method m, Object[] args) dans votre propre code.


EDIT: D'après les commentaires, il semble que la classe que vous ne voulez pas est l'interface classe, pas la classe de mise en œuvre . Dans ce cas, il n'y a aucun moyen de contourner la manipulation du code d'octet. Vous pouvez regarder Javassist qui contient un compilateur d'extraits.

+0

merci, mais cet exemple a besoin d'une implémentation de l'interface et je ne veux pas créer une implémentation réelle. – virgium03

+0

c'est ce que j'essaie d'éviter. – virgium03

0

Dans votre code je l'ai fait ce qui suit

//    return thisMethod.invoke(self, args); 
      return null; 

et j'ai obtenu le résultat suivant

Handling public abstract void org.rege.instruments.IPerson.setName(java.lang.String) via the method handler 

Est-ce que vous avez voulu? Comme vous pouvez le voir OutOfMemory a été produit par un appel récursif à votre appel.

8

Vous pouvez utiliser la méthode newProxyInstance de java.lang.reflect.Proxy. Ex:

Proxy.newProxyInstance(iClazz.getClassLoader(), 
     new Class[]{iClazz}, 
     new YourInvocationHandler()) 

iClazz est la classe de votre interface et YourInvocationHandler est une instance de java.lang.reflect.InvocationHandler

Questions connexes