Design Pattern: Proxy 模式
来看看实现代理的两种方式:Static Proxy与Dynamic Proxy。严格来说这是属于模式的实现方式,不过藉由实例可以更了解Proxy模式的应用。
先来看个例子,这个例子是记录(log)动作,程式中很常需要为某些动作或事件作下记录,以便在事后检视或是作为除错时的资讯,一个最简单的例子如下:
import java.util.logging.*; public class HelloSpeaker { private Logger logger = Logger.getLogger(this.getClass().getName()); public void hello(String name) { logger.log(Level.INFO, "hello method starts...."); System.out.println("Hello, " + name); logger.log(Level.INFO, "hello method ends...."); } }
HelloSpeaker在执行hello()方法时,您希望能记录该方法已经执行及结束,最简单的作法就是如上在执行的前后加上记录动作,然而 Logger介入了HelloSpeaker中,记录这个动作并不属于HelloSpeaker,这使得HelloSpeaker增加了非业务上需要的逻辑在当中。
想想如果程式中这种记录的动作到处都有需求,上面这种写法势必造成必须复制记录动作的程式码,使得维护记录动作的困难度加大。如果不只有记录动作,有一些非物件本身职责的相关动作也混入了物件之中(例如权限检查、事务管理等等),会使得物件的负担更形加重,甚至混淆了物件的职责,物件本身的职责所占的程式码,或许远小于这些与物件职责不相关动作的程式码。
怎么办,用下面的方法或许好一些,先定义一个介面,然后实作该介面:
public interface IHello { public void hello(String name); } ?
HelloSpeaker.javapublic class HelloSpeaker implements IHello { public void hello(String name) { System.out.println("Hello, " + name); } }
接下来实作一个代理物件HelloProxy:
import java.util.logging.*; public classIHello.java
InvocationHandler的invoke()方法会传入被代理物件的方法名称与执行参数实际上要执行的方法交由method.invoke (),并在其前后加上记录动作,method.invoke()传回的物件是实际方法执行过后的回传结果。
Dynamic Proxy必须宣告介面,实作该介面,例如:public interface IHello { public void hello(String name); }?
HelloSpeaker.javapublic class HelloSpeaker implements IHello { public void hello(String name) { System.out.println("Hello, " + name); } }LogHandler logHandler = new LogHandler();
java.lang.reflect.Proxy的newProxyInstance()依要代理的物件、介面与handler产生一个代理物件,我们可以使用下面的方法来执行程式:
IHello helloProxy = (IHello) logHandler.bind(
new HelloSpeaker());
helloProxy.hello("Justin");
LogHandler不在服务于特定物件与介面,而HelloSpeaker也不用插入任何有关于记录的动作,它不用意识到记录动作的存在。上文写的动态代理没有理解真正的代理,特指的是一个抽象类或者接口去实现代理。
?
接口
package com.ijo.proxy;
public interface IHello {
??? void sayHello(String name);
}实现类
package com.ijo.proxy;
public class EnglishSay implements IHello {
?public void sayHello(String name) {
??System.out.println(name + " say hello word!!!");
?}}
代理类package com.ijo.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;import com.ijo.proxy.EnglishSay;
import com.ijo.proxy.IHello;public class StudyTest implements InvocationHandler {
?private IHello delegate;
?
public Object bind(IHello delegate) {
??this.delegate = delegate;
??return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),
????delegate.getClass().getInterfaces(), this);
?}?
?public Object invoke(Object proxy, Method method, Object[] objs)
???throws Throwable {
??Object result = null;
??System.out.println("this is say begin");
??result = method.invoke(delegate, objs);
??System.out.println("this is say end");
??return result;
?}?
?public static void main(String[] args) {
??StudyTest studyTest = new StudyTest();
??IHello hello = (IHello) studyTest.bind(new EnglishSay());
??hello.sayHello("chinaxxren ");
?}
}结果:
this is say begin
chinaxxren? say hello word!!!
this is say end
?