动态代理模式

来源:互联网 发布:陕西广电网络官网 编辑:程序博客网 时间:2024/05/16 09:39

代理模式

代理是基本的设计模式之一,它是你为了提供额外的或不同的操作,而插入的用来代替“实际”对象的对象。这些操作通常涉及与“实际”对象的通信,因此代理通常充当着中间人的角色。

基本代理

下面是一个用来展示代理结构的简单示例:
public interface Interface {
void doSomething();
 
void somethingElse(String arg);
}
class RealObject implements Interface {
 
@Override
public void doSomething() {
System.out.println("doSomething");
}
 
@Override
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
}
}
 
class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
 
@Override
public void doSomething() {
System.out.println("doSomething");
proxied.doSomething();
}
 
@Override
public void somethingElse(String arg) {
System.out.println("somethingElse " + arg);
proxied.somethingElse(arg);
}
}
 
public class SimpelProxyDemo {
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()));
}
 
}
因为consumer()接受的Interface,所以它无法知道正在获得的到底是RealObject还是SimpleProxy,因为这二者都实现了Interface。但是SimpleProxy已经被插入到了客户端和RealObject之间,因此它会执行操作,然后调用RealObject上相同的方法。
在任何时刻,只要你想要将额外的操作从“实际”对象中分离到不同的地方,特别是当你希望能够很容易地做出修改,从没有使用额外操作转为使用这些操作,或者反过来时,代理就显得很有用(设计模式的关键就是封装修改----因此你需要修改事务以证明这种模式的正确性)。例如,如果你希望跟踪对RealObject中的方法的调用,或者希望度量这些调用的开销,那么你应该怎么做呢?这些代码肯定是你不希望将其合并到应用中的代码,因此代理使得你可以很容易地添加或移除它们。

动态代理

Java的动态代理比代理的思想更向前迈进了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用的类型并确定相应的对策。下面是用动态代理重写的SimpleProxyDemo.java
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
// 调用处理器 
@Override
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);
}
}
 
public class SimpelProxyDemo {
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()));
RealObject real = new RealObject();
consumer(real);
Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class[]{Interface.class}, new DynamicProxyHandler(real));
consumer(proxy);
}
 
}
通过调用静态方法Proxy.newProxyInstance()可以创建动态代理,这个方法需要得到一个类加载器(你通常可以从已经被加载的对象中获取其类加载器,然后传递给它),一个你希望该代理实现的接口列表(不是类或抽象类),以及InvocationHandler接口的一个实现。动态代理可以将所有调用重定向到调用处理器,因此通常会向调用处理器的构造器传递给一个“实际”对象的引用,从而使得调用处理器在执行其中介任务时,可以将请求转发。
invoke()方法中传递进来了代理对象,以防你需要区分请求的来源,但是在许多情况下,你并不关心这一点。然而,在invoke()内部,在代理上调用方法时需要格外当心,因为对接口的调用将被重定向为对代理的调用。

过滤代理方法

通常,你会执行被代理的操作,然后使用Method.invoke()将请求转发给被代理对象,并传入必需的参数。这初看起来可能有些受限,就像你只能执行泛化操作一样。但是,你可以通过传递其他的参数,来过滤某些方法调用:
class MethodSelector implements InvocationHandler {
private Object proxied;
public MethodSelector(Object proxied) {
this.proxied = proxied;
}
 
@Override
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 {
 
@Override
public void boring1() {
System.out.println("boring1");
}
 
@Override
public void boring2() {
System.out.println("boring2");
}
 
@Override
public void interesting(String arg) {
System.out.println("interesting arg:" + arg);
}
 
@Override
public void boring3() {
System.out.println("boring3");
}
}
 
public class SimpelProxyDemo {
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();
}
}
这里,我们只查看了方法名,但是你还可以查看方法签名的其他方面,甚至可以搜索特定的参数值。


0 0
原创粉丝点击