Выполните метод вызова InvocationHandler для каждого метода в моем прокси-классе
Я реализовал динамический прокси для выполнения некоторых операций до того, как мои методы начали. Теперь у меня проблема при вызове двух методов из прокси-класса, вот код:
Динамический прокси-класс:
public class IPageProxy implements InvocationHandler {
private Class <? extends IPage> screenClazz;
public IPageProxy(final Class <? extends IPage> screenClazz) {
this.screenClazz = screenClazz;
}
@SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type)
throws InstantiationException, IllegalAccessException {
List<Class<?>> interfaces = new ArrayList<>();
interfaces.addAll(Arrays.asList(type.getInterfaces()));
return (T) Proxy.newProxyInstance(
type.getClassLoader(),
findInterfaces(type),
new IPageProxy(type)
);
}
static Class<?>[] findInterfaces(final Class<? extends IPage> type) {
Class<?> current = type;
do {
final Class<?>[] interfaces = current.getInterfaces();
if (interfaces.length != 0) {
return interfaces;
}
} while ((current = current.getSuperclass()) != Object.class);
throw new UnsupportedOperationException("The type does not implement any interface");
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws InvocationTargetException,
IllegalAccessException, IllegalArgumentException, InstantiationException, ParserConfigurationException, XPathExpressionException, NoSuchFieldException, SecurityException {
// before method executed this code will be done
System.out.println("* Dynamic proxy invoke method executed for " + method.getName());
// Invoke original method
return method.invoke(screenClazz.newInstance(), args);
}
}
Основной класс:
public static void main(String[] args) {
try {
//IEventDesignDialog a = new EventDesignDialog();
IEventDesignDialog a = (IEventDesignDialog)getInstance(EventDesignDialog.class);
a.getEventType().getShow();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.getInstance(type);
}
Прокси-класс:
public class EventDesignDialog implements IEventDesignDialog{
private String show;
private String dateAndTimeDisplayFormat;
private String eventType;
@Entity(visibileName = "Show")
public IEventDesignDialog getShow() {
System.out.println("get show method invokde successfully");
return this;
}
@Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invokde successfully");
return this;
}
@Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType() {
System.out.println("get event type method invokde successfully");
return this;
}
}
Фактический вывод:
*** Dynamic proxy invoke method executed for getEventType
get event type method invokde successfully
get show method invokde successfully**
как показано, метод invoke выполняется только при первом вызове метода после инициализации прокси, второй метод вызывается напрямую, без функции прокси.
Моя цель - выполнять метод invoke каждый раз, когда вызывается метод из моей коллекции, ожидаемый результат должен быть таким, как показано ниже.
Ожидаемый результат:
*** Dynamic proxy invoke method executed for getEventType
get event type method invokde successfully
* Dynamic proxy invoke method executed for getShow
get show method invokde successfully**
пожалуйста, дайте мне знать, если нужны дополнительные объяснения.
2 ответа
Происходит то, что сначала вы проксируете только первый вызов, а затем вызываете getShow() для класса без прокси, и именно поэтому вы получаете результат, как вы упомянули. Если вы хотите достичь цели, которую вы упомянули, вам нужно создать другой прокси на основе созданного экземпляра, а не только на классе.
Обновление: я приведу пример кода, вы можете вставить в любой файл Java и выполнить его. Там, где вы видите TODO, вы можете разместить свою собственную логику в зависимости от того, как вы хотите предоставить прокси. Смотрите ПРИМЕЧАНИЕ для важных моментов. Я поместил все классы в один файл для простоты демонстрации.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
class Scratch {
public static void main(String[] args) {
try {
IEventDesignDialog a = proxy(EventDesignDialog.class);
a.getEventType().getShow();
a.getDateAndTimeDisplayFormat().getShow();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
private static <T extends IPage> T proxy(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.proxy(type);
}
}
interface IPage{}
interface IEventDesignDialog extends IPage{
IEventDesignDialog getShow();
IEventDesignDialog getEventType();
IEventDesignDialog getDateAndTimeDisplayFormat();
}
class EventDesignDialog implements IEventDesignDialog{
public IEventDesignDialog getShow() {
System.out.println("get show method invoked successfully");
//NOTE: this will be treated as same proxy but not this
return this;
}
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invoked successfully");
// NOTE: we supply some iinstance which will be proxied
return new MyIEventDesignDialog();
}
public IEventDesignDialog getEventType() {
System.out.println("get event type method invoked successfully");
//NOTE: this will be treated as same proxy but not this
return this;
}
}
class IPageProxy implements InvocationHandler {
private IPage instance;
private List<Class<?>> interfaces;
public IPageProxy(IPage instance, List<Class<?>> interfaces) {
this.instance = instance;
this.interfaces = interfaces;
}
@SuppressWarnings("unchecked")
public static <T extends IPage> T proxy(final Class<? extends IPage> type)
throws InstantiationException, IllegalAccessException {
List<Class<?>> interfaces = Arrays.asList(type.getInterfaces());
//TODO: get interfaces properly recursively
return (T) Proxy.newProxyInstance(
type.getClassLoader(),
type.getInterfaces(),
new IPageProxy(type.newInstance(), interfaces)
);
}
@SuppressWarnings("unchecked")
public static <T extends IPage> T proxy(T object) {
//TODO: get interfaces properly recursively
List<Class<?>> interfaces = Arrays.asList(object.getClass().getInterfaces());
return (T) Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new IPageProxy(object, interfaces)
);
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Exception {
// before method executed this code will be done
System.out.println("* Dynamic proxy invoke method executed for " + method.getName());
// Invoke original method
Object invoke = method.invoke(instance, args);
if (invoke == null) {
return null;
}
//If some of the method returns the original object
//we swap the returned object by our proxy
if (invoke == instance) {
return proxy;
}
//TODO: check if you want to swap in place
//other interfaces
if (interfaces.contains(method.getReturnType())) {
return IPageProxy.proxy((IPage)invoke);
}
return invoke;
}
}
class MyIEventDesignDialog implements IEventDesignDialog {
@Override
public IEventDesignDialog getShow() {
return null;
}
@Override
public IEventDesignDialog getEventType() {
return null;
}
@Override
public IEventDesignDialog getDateAndTimeDisplayFormat() {
return null;
}
}
Выход:
* Dynamic proxy invoke method executed for getEventType
get event type method invoked successfully
* Dynamic proxy invoke method executed for getShow
get show method invoked successfully
* Dynamic proxy invoke method executed for getDateAndTimeDisplayFormat
get date and time display format method invoked successfully
* Dynamic proxy invoke method executed for getShow
Вы можете получить идеи о том, как работает Mockito. Пожалуйста, проверьте эту страницу: https://static.javadoc.io/org.mockito/mockito-core/2.27.0/org/mockito/Mockito.html
Я знаю, что это для тестирования, но вы все еще можете получить идеи из этого. Таким образом, вы можете применить spy() к классу и к объекту, чтобы шпионить за ним.
Я решил эту проблему, создав интерфейс с методом по умолчанию, который возвращает экземпляр прокси, а затем возвратил его после выполнения функциональности вызванного метода:
обновленный код:
public interface IPage {
default <T extends IPage> T getProxyInstance() {
try {
return (T) IPageProxy.getInstance(this.getClass());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
Интерфейс моей страницы:
@Page(path = "MyPath")
public interface IEventDesignDialog extends IPage{
@Entity(visibileName = "Show")
public IEventDesignDialog getShow();
@Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat();
@Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType();
}
Класс моей страницы:
@Page(path = "MyPath")
public class EventDesignDialog implements IEventDesignDialog{
@Entity(visibileName = "Show")
public IEventDesignDialog getShow() {
System.out.println("get show method invokde successfully");
return getProxyInstance();
}
@Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invokde successfully");
return getProxyInstance();
}
@Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType() {
System.out.println("get event type method invokde successfully");
return getProxyInstance();
}
}
основной класс:
public class Main {
public static void main(String[] args) {
try {
IEventDesignDialog a = ((IEventDesignDialog)getInstance(EventDesignDialog.class)).getEventType().getShow();
((IShowDesignDialog)getInstance(ShowDesignDialog.class)).getShowName().getShowType();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.getInstance(type);
}
}
Страница IProxy остается без изменений.