您的位置:首页 > 编程语言 > Java开发

深入解析Java设计模式之动态代理

2014-08-07 17:58 1331 查看

深入解析Java设计模式之动态代理

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

/**
普通(非动态)代理示例:
*/

interface Interface {
void doSomething();
void somethingElse(String arg);
}

class RealObject implements Interface {
public void doSomething() { print("doSomething"); }
public void somethingElse(String arg) {
print("somethingElse " + arg);
}
}

class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
public void doSomething() {
//自定义执行逻辑部分
print("SimpleProxy doSomething");
//调用“被代理”对象的方法
proxied.doSomething();
}
public void somethingElse(String arg) {
//自定义执行逻辑部分
print("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
*///:~

因为cousumer()接受的Interface,所以他无法知道正在获得的到底是RealObject还是SimpleProxy,因为这二者都实现了Interface.但是SimpleProxy已经插入到了客户端和RealObject之间,因此他会执行操作,然后调用RealObject上相同的方法。
在任何时刻,只要你想要将额外的操作”实际“对象中分离到不同的地方,特别是当你希望能够很容易地做出修改,从没有使用额外操作转为使用这些操作或者反过来时,代理就显得很有用(设计模式的关键就是封装修改——因此你需要修改事物以证明这种模式的正确性)。列如,如果你希望跟踪RealObject中的方法的调用,或者希望度量这些调用的开销,那么你应该怎么做呢?这些代码肯定是你不希望将其合并到应用中的代码,因此代理使得你可以很容易地移除或移除它们。

Java的动态代理的思想向前更迈进了一步,因为它可以动态地创建代理并动态地处理对所代理方法的调用。在动态代理商所做的所有调用都会被重定向到单一的调用处理器上,它的工作是揭示调用额类型并确定相应的对策。下面是用动态代理重写的SimpleProxyDemo.java:

/**
动态代理示例:
*/
import java.lang.reflect.*;

class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Objectinvoke(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.newProxyInstance()可以创建动态代理,这个方法需要的到一个类加载器(你通常可以才从已经被加载额对象中获得其类加载器,然后传递它),一个你希望该代理实现的接口列表(不是类或抽象类),以及Invocationhandler接口的一个实现。动态代理可以将所有的调用重定向到调用处理器,因此通常会向调用处理器的构造器传递一个”实际“对象的引用,从而使得调用处理器在执行其中任务时,可以将请求转发。

invoke()方法中传递进来了代理对象,以防你需要区分请求的来源,但是在许多情况下,你并不关心这一点。然而,在invoke()内部,在代理上调用方法时需要格外当心,因为对接口的调用被重定向为对代理的调用

通常,你会执行被代理的操作,然而使用Method.invoke()将请求转发给被代理对象,并传入必须的参数。这初看起来可能有些受限,就像你只能执行泛化操作一样。但是,你可以通过传递其他的参数,来过滤某些方法调用:

动态代理和普通代理(非动态代理)一个最主要的区别就是代理类代码不需要你手动实现了,比如第一个示例中的"SimpleProxy"类就不需要手动实现。

     动态代理原理(序号表明了执行顺序):

   (1).通过Proxy类的newProxyInstance()方法动态地生成代理类——>

   (2).接口调用的重定向(对接口的调用将被重定向为对代理的调用)——>

   (3).在"invoke"方法中在method.invoke(....)之前或之后插入自定义逻辑代码——>

   (4).通过反射技术执行被代理的方法。

/**
动态代理(执行指定方法)示例
*/
import java.lang.reflect.*;
import static net.mindview.util.Print.*;

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 {
//此处过滤掉了不包含有关键字“interesting”的方法
if(method.getName().equals("interesting"))
print("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() { print("boring1"); }
public void boring2() { print("boring2"); }
public void interesting(String arg) {
print("interesting " + arg);
}
public void boring3() { print("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
*///:~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息