web-dev-qa-db-fra.com

Comment obtenir le type sous-jacent d'un objet proxy en Java?

J'aimerais accéder au nom de classe de la classe sous-jacente, qui est une instance de Java.lang.reflect.Proxy.

Est-ce possible?

26
blank

Vous pouvez obtenir la InvocationHandler avec laquelle le proxy a été créé en appelant Proxy.getInvocationHandler(proxy)

Notez que dans le cas de Java.lang.reflect.Proxy, il n'y a pas de classe sous-jacente en soi. Le proxy est défini par:

  • interface (s)
  • gestionnaire d'invocation

Et la classe wrapped est généralement transmise au gestionnaire d'invocation concret.

22
Bozho

J'ai trouvé une bonne solution sur ce site (maintenant archivé):

@SuppressWarnings({"unchecked"})
protected <T> T getTargetObject(Object proxy, Class<T> targetClass) throws Exception {
  if (AopUtils.isJdkDynamicProxy(proxy)) {
    return (T) ((Advised)proxy).getTargetSource().getTarget();
  } else {
    return (T) proxy; // expected to be cglib proxy then, which is simply a specialized class
  }
}

Usage

@Override
protected void onSetUp() throws Exception {
  getTargetObject(fooBean, FooBeanImpl.class).setBarRepository(new MyStubBarRepository());
}
20
Sllouyssgort

Une instance de proxy ne sera pas une instance de Java.lang.reflect.Proxy en soi . Il s'agira plutôt d'une instance de sous-classe de Java.lang.reflect.Proxy.

Quoi qu’il en soit, la manière d’obtenir le nom réel des classes proxy est la suivante:

Proxy proxy = ...
System.err.println("Proxy class name is " + proxy.getClass().getCanonicalName());

Cependant, vous ne pouvez pas obtenir le nom de la classe pour laquelle le proxy est un proxy, car:

  1. vous des interfaces de proxy pas des classes, et
  2. un proxy peut être un proxy pour plusieurs interfaces

Cependant, en regardant le code source de la classe ProxyGenerator, il semble que les interfaces soient enregistrées dans la classe proxy générée en tant qu'interfaces de la classe. Vous devriez donc pouvoir les obtenir au moment de l'exécution via l'objet classes Class de proxy; par exemple.

Class<?>[] classes = proxy.getClass().getInterfaces();

(Note: je n'ai pas essayé ça ...)

15
Stephen C

Voici la solution que nous avons utilisée avec mon équipe (nous avons besoin du nom de la classe derrière le proxy):

if (getTargetName(yourBean) ... ) {

}

Avec cette petite aide:

private String getTargetName(final Object target) {

    if (target == null) {
        return "";
    }

    if (targetClassIsProxied(target)) {

        Advised advised = (Advised) target;

        try {

            return advised.getTargetSource().getTarget().getClass().getCanonicalName();
        } catch (Exception e) {

            return "";
        }
    }

    return target.getClass().getCanonicalName();
}

private boolean targetClassIsProxied(final Object target) {

    return target.getClass().getCanonicalName().contains("$Proxy");
}

J'espère que ça aide!

5
lboix

Vous pouvez utiliser le code suivant pour récupérer les informations (ArrayUtils provient d'Apache commons lang) sur le gestionnaire d'appel et les interfaces du proxy actuel:

String.format("[ProxyInvocationHandler: %s, Interfaces: %s]", 
     Proxy.getInvocationHandler(proxy).getClass().getSimpleName(), 
     ArrayUtils.toString(proxy.getClass().getInterfaces()));

Exemple de résultat:

[ProxyInvocationHandler: ExecuteProxyChain, Interfaces: {interface com.example.api.CustomerApi}]}
3
Przemek Nowak

Tout d'abord, Java.lang.reflect.Proxy ne fonctionne que sur les interfaces. L'infrastructure crée une classe descendante qui implémente l'interface ou les interfaces mais étend Java.lang.reflect.Proxy plutôt qu'une classe d'application susceptible de vous intéresser. Il n'y a pas d'héritage de plusieurs classes dans Java moderne (2016).

Dans mon cas, le débogueur montre que l'objet d'intérêt est dans le champ obj du gestionnaire d'invocation de l'objet proxy.

    Object handler = Proxy.getInvocationHandler(somethingProxied);
    Class handlerClass = handler.getClass();
    Field objField = handlerClass.getDeclaredField("obj");
    objField.setAccessible(true);
    Object behindProxy = objField.get(handler);

Vous devrez catch() deux exceptions: NoSuchFieldException et IllegalAccessException.

J'ai trouvé utile d'imprimer la liste des champs des champs déclarés à partir de la clause catch():

...
} catch (NoSuchFieldException nsfe) {
    nsfe.printStackTrace();

    Object handler = Proxy.getInvocationHandler(somethingProxied);
    Class handlerClass = handler.getClass();
    for (Field f : handlerClass.getDeclaredFields()) {
        f.setAccessible(true);
        String classAndValue = null;
        try {
            Object v = f.get(handler);
            classAndValue= "" + (v == null ? "" : v.getClass()) + " : " + v;
        } catch (IllegalAccessException iae) {
            iae.printStackTrace();
        }
        System.out.println(" field: " + f.getName() + " = " + classAndValue+ ";");
    }
...
}

Notez que différents frameworks utilisent différents proxy et même différentes techniques de proxy. La solution qui a fonctionné pour moi peut ne pas être applicable dans votre cas. (Cela ne fonctionnera certainement pas pour les mandataires Javassist ou Hibernate.)

0