Подтвердить что ты не робот

Как получить базовый тип прокси-объекта в java?

Мне бы хотелось получить доступ к имени класса базового класса, который является экземпляром java.lang.reflect.Proxy.

Возможно ли это?

4b9b3361

Ответ 1

Вы можете получить InvocationHandler, с которым был создан прокси, путем вызова Proxy.getInvocationHandler(proxy)

Обратите внимание, что в случае java.lang.reflect.Proxy основной класс не существует. Прокси определяется следующим образом:

  • интерфейс (ы)
  • обработчик вызова

И обернутый класс обычно передается конкретному обработчику вызовов.

Ответ 2

Я нашел хорошее решение на этом сайте (теперь архивировано):

@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
  }
}

Использование

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

Ответ 3

Ну, экземпляр Proxy не будет экземпляром java.lang.reflect.Proxy per se. Скорее, это будет экземпляр подкласса java.lang.reflect.Proxy.

В любом случае, способ получить фактическое имя класса прокси-сервера:

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

Однако вы не можете получить имя класса, для которого прокси является прокси-сервером, потому что:

  • вы прокси-интерфейсы не классы, а
  • Прокси может быть прокси для нескольких интерфейсов

Однако, глядя на исходный код класса ProxyGenerator, кажется, что интерфейсы записываются в сгенерированный прокси-класс в качестве интерфейсов класса. Таким образом, вы должны иметь возможность запускать их во время выполнения через прокси-классы Class object; например.

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

(Примечание: я не пробовал это...)

Ответ 4

Вот решение, которое мы использовали с моей командой (нам нужно имя класса за прокси):

if (getTargetName(yourBean) ... ) {

}

С помощью этого небольшого помощника:

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");
}

Надеюсь, что это поможет!

Ответ 5

Вы можете использовать следующий код для получения информации (ArrayUtils из Apache commons lang) об обработчике вызовов и интерфейсах текущего прокси:

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

Результат:

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

Ответ 6

Прежде всего, java.lang.reflect.Proxy работает только на интерфейсах. Структура создает класс потомков, который реализует интерфейс, но расширяет java.lang.reflect.Proxy, а не класс приложения, который может вас заинтересовать. В современной (2016) Java не существует наследования нескольких классов.

В моем случае отладчик показывает, что объект интереса находится в поле obj в обработчике вызова прокси-объекта.

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

Вам придется catch() два исключения: NoSuchFieldException и IllegalAccessException.

Мне было полезно распечатать список полей объявленных полей из предложения 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+ ";");
    }
...
}

Обратите внимание, что разные структуры используют разные прокси и даже разные методы проксирования. Решение, которое сработало для меня, может быть неприменимо в вашем случае. (Это определенно не будет работать для прокси-серверов Javassist или Hibernate.)