Мне бы хотелось получить доступ к имени класса базового класса, который является экземпляром java.lang.reflect.Proxy
.
Возможно ли это?
Мне бы хотелось получить доступ к имени класса базового класса, который является экземпляром java.lang.reflect.Proxy
.
Возможно ли это?
Вы можете получить InvocationHandler
, с которым был создан прокси, путем вызова Proxy.getInvocationHandler(proxy)
Обратите внимание, что в случае java.lang.reflect.Proxy
основной класс не существует. Прокси определяется следующим образом:
И обернутый класс обычно передается конкретному обработчику вызовов.
Я нашел хорошее решение на этом сайте (теперь архивировано):
@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());
}
Ну, экземпляр 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();
(Примечание: я не пробовал это...)
Вот решение, которое мы использовали с моей командой (нам нужно имя класса за прокси):
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");
}
Надеюсь, что это поможет!
Вы можете использовать следующий код для получения информации (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}]}
Прежде всего, 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.)