首页 > 代码库 > 深入解析Java设计模式之动态代理
深入解析Java设计模式之动态代理
深入解析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 *///:~