代理模式:代理是基本的设计模式之一,它为你提供额外的或不同的操作,而插入的用来代替实际对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常当着中间人的角色。
interface Interface {
void doSomething();
void somethingElse(String arg);
}
class RealObject implements Interface {
public void doSomething() { System.out.println("doSomething"); }
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
}
}
class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
public void doSomething() {
System.out.println("SimpleProxy doSomething");
proxied.doSomething();
}
public void somethingElse(String arg) {
System.out.println("SimpleProxy somethingElse " + arg);
proxied.somethingElse(arg);
}
}
class SimpleProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
consumer(new RealObject());
consumer(new SimpleProxy(new RealObject()));
}
} /* Output:
doSomething
somethingElse bonobo
SimpleProxy doSomething
doSomething
SimpleProxy somethingElse bonobo
somethingElse bonobo
*///:~
因为consumer()接受的Interface,所以它无法知道正在获得的到底是Rea.lObject还是SimpleProxy, 因为这二者都实现了Interface。但是SimpleProxy已经被插人到了客户端和 RealObject之间, 因此它会执行操作, 然后调用RealObject上相同的方法。
在任何时刻,只要你想要将额外的操作从 “实际” 对象中分离到不同的地方, 特别是当你希望能够很容易地做出修改, 从没有使用额外操作转为使用这些操作? 或者反过来时,代理就显得很有用(设计模式的关键就是封装修改一因此你需要修改事务以证明这种模式的正确性)。 例如, 如果你希望跟踪对RealObject中的方法的调用, 或者希望度扯这些调用的开销, 那么你 应该怎样做呢?这些代码肯定是你不希望将其合并到应用中的代码, 因此代理使得你可以很容 易地添加或移除它们。
Java的动态代理比代理的思想更向前迈进了一步, 因为它可以动态地创建代理并动态地处理
回 对所代理方法的调用。 在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。
import java.lang.reflect.*;
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("**** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null)
for(Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
}
class SimpleDynamicProxy {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("bonobo");
}
public static void main(String[] args) {
RealObject real = new RealObject();
consumer(real);
// Insert a proxy and call again:
Interface proxy = (Interface)Proxy.newProxyInstance(
Interface.class.getClassLoader(),
new Class[]{ Interface.class },
new DynamicProxyHandler(real));
consumer(proxy);
}
} /* Output: (95% match)
doSomething
somethingElse bonobo
**** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null
doSomething
**** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816
bonobo
somethingElse bonobo
*///:~
通过调用静态方法Proxy.newProxylnstanceO可以创建动态代理,这个方法蒂要得到一个类加载器(你通常可以从已经被加载的对象中获取其类加载器,然后传递给它),一个你希望该代理实现的接口列表(不是类或抽象类), 以及InvocationHandler接口的一个实现。 动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递给一个 “实际” 对象的引用, 从而使得调用处理器在执行其中介任务时, 可以将请求转发。
invoke()方法中传递进来了代理对象,以防你需要区分请求的来源,但是在许多情况下, 你
并不关心这一点。 然而,在invoke()内部,在代理上调用方法时需要额外小心,因为对接口的调用将重定向为对代理的调用。
通常,你会执行被代理的操作, 然后使用Method.invoke()将请求转发给被代理对象,井传
入必需的参数。 这初看起来可能有些受限,就像你只能执行泛化操作一样。但是你可以通过传递其他参数,来过滤某写方法的调用,如下例子:
import java.lang.reflect.*;
class MethodSelector implements InvocationHandler {
private Object proxied;
public MethodSelector(Object proxied) {
this.proxied = proxied;
}
public Object
invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("interesting"))
System.out.println("Proxy detected the interesting method");
return method.invoke(proxied, args);
}
}
interface SomeMethods {
void boring1();
void boring2();
void interesting(String arg);
void boring3();
}
class Implementation implements SomeMethods {
public void boring1() { System.out.println("boring1"); }
public void boring2() { System.out.println("boring2"); }
public void interesting(String arg) {
System.out.println("interesting " + arg);
}
public void boring3() { System.out.println("boring3"); }
}
class SelectingMethods {
public static void main(String[] args) {
SomeMethods proxy= (SomeMethods)Proxy.newProxyInstance(
SomeMethods.class.getClassLoader(),
new Class[]{ SomeMethods.class },
new MethodSelector(new Implementation()));
proxy.boring1();
proxy.boring2();
proxy.interesting("bonobo");
proxy.boring3();
}
} /* Output:
boring1
boring2
Proxy detected the interesting method
interesting bonobo
boring3
*///:~
这里我们只查看了方法名,但是你可以查看方法签名的其他方面,甚至可以搜索特定的参数值。