Вы можете реализовать прокси с обычными классами, например,
Iterable<String> i = Arrays.asList("hello", "world");
Iterable<String> proxy = new Iterable<String>() {
@Override
public Iterator<String> iterator() {
System.out.println("Entering .iterator()");
try {
return i.iterator();
}
finally {
System.out.println("Exiting .iterator()");
}
}
};
for(String s: proxy) {
System.out.println(s);
}
Entering .iterator()
Exiting .iterator()
hello
world
Преимущество генераторов прокси-серверов Dynami c, таких как java.lang.reflect.Proxy
, заключается в том, что они позволяют обрабатывать произвольное число интерфейсов. методы с одним методом-обработчиком и не требуют жесткого кодирования интерфейсов для реализации. Интерфейсы даже не должны существовать во время компиляции.
Например:
List<String> i = Arrays.asList("hello", "world");
final class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object t) { target = t; }
@Override public Object invoke(
Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getDeclaringClass().getSimpleName()+'.'+method.getName();
System.out.println(" Entering "+name);
try {
Object o = method.invoke(target, args);
final Class<?> rt = method.getReturnType();
if(rt != Void.TYPE) System.out.println(" => "+o);
return o == null || !rt.isInterface()? o:
Proxy.newProxyInstance(null,new Class<?>[]{rt},new MyInvocationHandler(o));
}
finally {
System.out.println(" Exiting "+name);
}
}
}
List<String> proxy = (List<String>)
Proxy.newProxyInstance(null, new Class<?>[]{List.class}, new MyInvocationHandler(i));
System.out.println("*** for loop:");
for(String s: proxy) System.out.println(s);
System.out.println();
System.out.println("*** stream:");
proxy.stream().forEach(System.out::println);
System.out.println();
System.out.println("*** parallel stream:");
StreamSupport.stream(proxy.spliterator(), true).forEach(System.out::println);
*** for loop:
Entering List.iterator
=> java.util.Arrays$ArrayItr@5cb0d902
Exiting List.iterator
Entering Iterator.hasNext
=> true
Exiting Iterator.hasNext
Entering Iterator.next
=> hello
Exiting Iterator.next
hello
Entering Iterator.hasNext
=> true
Exiting Iterator.hasNext
Entering Iterator.next
=> world
Exiting Iterator.next
world
Entering Iterator.hasNext
=> false
Exiting Iterator.hasNext
*** stream:
Entering Collection.stream
=> java.util.stream.ReferencePipeline$Head@443b7951
Exiting Collection.stream
Entering Stream.forEach
hello
world
Exiting Stream.forEach
*** parallel stream:
Entering List.spliterator
=> java.util.Spliterators$ArraySpliterator@5d6f64b1
Exiting List.spliterator
Entering Spliterator.characteristics
=> 16464
Exiting Spliterator.characteristics
Entering Spliterator.estimateSize
=> 2
Exiting Spliterator.estimateSize
Entering Spliterator.trySplit
=> java.util.Spliterators$ArraySpliterator@6aa8ceb6
Exiting Spliterator.trySplit
Entering Spliterator.estimateSize
=> 1
Exiting Spliterator.estimateSize
Entering Spliterator.getExactSizeIfKnown
=> 1
Exiting Spliterator.getExactSizeIfKnown
Entering Spliterator.forEachRemaining
world
Exiting Spliterator.forEachRemaining
Entering Spliterator.estimateSize
=> 1
Exiting Spliterator.estimateSize
Entering Spliterator.getExactSizeIfKnown
=> 1
Exiting Spliterator.getExactSizeIfKnown
Entering Spliterator.forEachRemaining
hello
Exiting Spliterator.forEachRemaining
Это демонстрирует, как один метод-обработчик может украшать интерфейс List
двумя дюжина методов и динамическое декорирование возвращаемых значений, когда типом возврата является интерфейс, украшающий четыре интерфейса в примере кода, и он будет декорировать больше при изменении тестового примера.
Просто попробуйте реализовать это с помощью обычных реализаций эти интерфейсы, и вы сразу же поймете преимущество генератора прокси Dynami c. Не говоря уже о возможности адаптации к новым интерфейсам при появлении…
Другим вариантом использования будет динамическое связывание компонентов c. Это может выглядеть так:
JButton component = new JButton("example");
String event = "action";
BeanInfo bi = Introspector.getBeanInfo(component.getClass());
for(EventSetDescriptor ed: bi.getEventSetDescriptors()) {
if(ed.getName().equals(event)) {
System.out.println("listening to "+ed.getDisplayName());
final Class<?> listener = ed.getListenerType();
ed.getAddListenerMethod().invoke(component,
Proxy.newProxyInstance(listener.getClassLoader(), new Class<?>[]{listener},
new InvocationHandler() {
@Override
public Object invoke(
Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("event "+method.getName()+Arrays.toString(args));
return null;
}
})
);
}
}
component.doClick();
listening to action
event actionPerformed[java.awt.event.ActionEvent[ACTION_PERFORMED,cmd=example,when=1579865741229,modifiers=] on javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.swing.plaf.BorderUIResource$CompoundBorderUIResource@5fdba6f9,flags=296,maximumSize=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintBorder=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rolloverSelectedIcon=,selectedIcon=,text=example,defaultCapable=true]]
Уже есть java.beans.EventHandler
, предоставляющий именно эту функцию для общего случая использования установки свойства другого компонента в ответ на событие.