Proxy模式为其他对象提供一种代理以控制对这个对象的访问。
类图
下面是一些可以使用Proxy模式常见情况:
- 远程代理(Remote Proxy)为一个对象在不同的地址空间提供局部代表。
- 虚代理(Virtual Proxy)根据需要创建开销很大的对象。
- 保护代理(Protection Proxy)控制对原始对象的访问。保护代理用于对象应该有不同的访问权限的时候。
- 智能指引(Smart Reference)取代了简单的指针,它在访问对象时执行一些附加操作。它的典型用途包括:对指向实际对象的引用计数,这样当该对象没有引用时,可以自动释放。
案例 1: Virtual Proxy
public interface FileHandler {
public void readFile();
}
public class TextFileHandler implements FileHandler {
private File file;
public TextFileHandler(String fileName) {
file = new File(fileName);
}
public void readFile() {
StringBuilder stringBuilder = new StringBuilder();
FileReader in = null;
try {
in = new FileReader(file);
BufferedReader buffIn = new BufferedReader(in);
String line = null;
while((line = buffIn.readLine()) != null) {
stringBuilder.append(line);
}
buffIn.close();
System.out.println(stringBuilder.toString());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class TextFileHandlerProxy implements FileHandler{
private TextFileHandler handler;
private String fileName;
public TextFileHandlerProxy(String fileName) {
this.fileName = fileName;
}
public FileHandler getHandler() {
if(handler == null) {
handler = new TextFileHandler(fileName);
}
return handler;
}
public void readFile() {
getHandler().readFile();
}
}
客户代码在创建TextFileHandlerProxy对象时,没有实例化被代理的对象 TextFileHandler,也就没有创建File对象,当调用readFile方法, 才开始创建被代理对象的一个拷贝,进而创建File对象。
另外一种方式,在代理对象中开启一个线程创建开销很大的被代理对象。
案例 2: RMI 远程代理, 分布式对象之间的通讯。
定义远程接口
public interface MyRemote extends Remote {
public String sayHello() throws RemoteException;
}
实现远程接口
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote {
protected MyRemoteImpl() throws RemoteException {
super();
}
public String sayHello() throws RemoteException {
return "Hello, World!";
}
public static void main(String[] args) {
try {
MyRemote service = new MyRemoteImpl();
Registry registry = LocateRegistry.createRegistry(8888);
registry.bind("RemoteHello", service); //Binds a remote reference to the specified name in this registry.
} catch (Exception e) {
e.printStackTrace();
}
}
}
访问远程对象
public class MyRemoteClient {
public static void main(String[] args) {
new MyRemoteClient().go();
}
public void go() {
try {
MyRemote service = (MyRemote)Naming.lookup("rmi://127.0.0.1:8888/RemoteHello");//反序列化返回Stub对象,客户端要有stub 类
System.out.println(service.sayHello());
} catch (Exception e) {
e.printStackTrace();
}
}
}
案例3: 通过JDK 动态代理 实现保护代理。
定义被代理对象(subject)的接口 (必须)
public interface Person {
String getName();
void setName(String name);
double getSalary();
void setSalary(double salary);
}
定义被代理对象
public class PersonImpl implements Person {
private String name;
private double salary;
public PersonImpl(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
salary = salary;
}
}
定义调用处理器,客户调用代理对象的方法,将被派遣到调用处理器invoke方法,再通过Method对象调用被代理对象的方法。
public class MyInvocationHandler implements InvocationHandler {
private Object target; //reference of subject
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().startsWith("get")) {
System.out.println("invoking method " + method.getName());
return method.invoke(person, args);
} else { //protect setter method
throw new IllegalAccessException("It is not accessable");
}
}
}
客户代码
public class ProxyClient {
public static void main(String[] args) {
Person person = new PersonImpl("Tom", 8000); //If use JDK dynamic proxy, subject must implement an interface
MyInvocationHandler handler = new MyInvocationHandler ();
Person proxy = (Person)handler.bind(person));
try {
proxy.setSalary(9999); // this method is protected, can not change salary
} catch (Exception e) {
System.out.println("message=" + e.getCause().getMessage());
}
}
}