代理可以控制和管理访问。
RMI提供了客户辅助对象和服务辅助对象,为客户辅助对象创建和服务对象相同的方法。RMI的好处在于你不必亲自写任何网络或I/O代码。客户程序调用远程方法就和运行在客户自己本地JVM对对象进行正常方法调用一样。
步骤一:制作远程接口
// 1 扩展java.rmi.Remote接口
// 2 声明所有方法都会抛出RemoteException
// 3 确定变量和返回值属于原语或者可序列化类型 (自己定义的类一定要实现Serializable)
import java.rmi.*
public interface MyRemote extends Remote{
public String sayHello() throws RemoteException;
}
步骤二:制作远程实现
// 1 客户必须实现远程接口
// 2 扩展UnicastRemoteObject对象,让超类帮你实现某些“远程”功能
// 3 声明一个不带变量的构造器,并抛出RemoteException
// 4 用RMI Registry注册此服务
//
public class MyRemoteImpl extends UnicastRemoteObject implements MyRemote{
public MyRemoteImpl() throws RemoteException{}
public String sayHello(){
return "Server says: hey";
}
public static void main (String[] args)
{
try{
MyRemote service = new MyRemoteImpl();
Naming.rebind("RemoteHello", service);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
步骤三:产生Stub和Skeleton
// (终端)在远程实现类上执行rmic
// rmic是JDK内的一个工具,用来为一个服务类产生stub和skeleton
%rmic MyRemoteImpl
步骤四:执行remiregistry
// 开启一个终端,启动rmiregistry
步骤五:启动服务
// 开启另一个终端,启动服务
// 可以从远程实现类的main方法,也可从一个独立的启动类
// 本文是从main方法启动,先实例化一个服务对象,然后到RMI registry注册
%java MyRemoteImpl
客户如何取得stub对象?
改造GunballMachine:
package headfirst.designpatterns.proxy.gumballmonitor;
public class GumballMonitor {
GumballMachine machine;
public GumballMonitor(GumballMachine machine) {
this.machine = machine;
}
public void report() {
System.out.println("Gumball Machine: " + machine.getLocation());
System.out.println("Current inventory: " + machine.getCount() + " gumballs");
System.out.println("Current state: " + machine.getState());
}
}
package headfirst.designpatterns.proxy.gumball;
import java.rmi.*;
public class GumballMonitorTestDrive {
public static void main(String[] args) {
String[] location = {"rmi://santafe.mightygumball.com/gumballmachine",
"rmi://boulder.mightygumball.com/gumballmachine",
"rmi://austin.mightygumball.com/gumballmachine"};
if (args.length >= 0)
{
location = new String[1];
location[0] = "rmi://" + args[0] + "/gumballmachine";
}
GumballMonitor[] monitor = new GumballMonitor[location.length];
for (int i=0;i < location.length; i++) {
try {
GumballMachineRemote machine =
(GumballMachineRemote) Naming.lookup(location[i]);
monitor[i] = new GumballMonitor(machine);
System.out.println(monitor[i]);
} catch (Exception e) {
e.printStackTrace();
}
}
for (int i=0; i < monitor.length; i++) {
monitor[i].report();
}
}
}
代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问。
使用代理模式创建代表对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象,创建开销大的对象或需要安全控制的对象。
虚拟代理应用场景:一个用于显示CD封面的应用程序,限于带宽和网络负载,下载需要一段时间,在这段时间内我们应该显示一些东西,在加载完成后显示真正的图像。
方法:虚拟代理用来代理icon,管理背景的加载,在加载未完成时显示“CD封面加载中,请稍后”,一旦加载完成,代理九八显示的职责委托给icon。
- ImageProxy首先创建一个ImageIcon,然后开始从URL上加载图像。
- 加载过程中,ImageProxy显示“CD封面加载中,请稍后...”。
- 当图像加载完毕,ImageProxy把所有方法调用委托给真正的ImageIcon(paintIcon, getWidth, getHeight)
- 若用户请求新的图像,就创建新的代理,重复以上过程。
package headfirst.designpatterns.proxy.virtualproxy;
import java.net.*;
import java.awt.*;
import javax.swing.*;
class ImageProxy implements Icon {
volatile ImageIcon imageIcon;
final URL imageURL;
Thread retrievalThread;
boolean retrieving = false;
public ImageProxy(URL url) { imageURL = url; }
public int getIconWidth() {
if (imageIcon != null) {
return imageIcon.getIconWidth();
} else {
return 800;
}
}
public int getIconHeight() {
if (imageIcon != null) {
return imageIcon.getIconHeight();
} else {
return 600;
}
}
synchronized void setImageIcon(ImageIcon imageIcon) {
this.imageIcon = imageIcon;
}
public void paintIcon(final Component c, Graphics g, int x, int y) {
if (imageIcon != null) {
imageIcon.paintIcon(c, g, x, y);
} else {
g.drawString("Loading album cover, please wait...", x+300, y+190);
if (!retrieving) {
retrieving = true;
retrievalThread = new Thread(new Runnable() {
public void run() {
try {
setImageIcon(new ImageIcon(imageURL, "Album Cover"));
c.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
});
retrievalThread = new Thread(() -> {
try {
setImageIcon(new ImageIcon(imageURL, "Album Cover"));
c.repaint();
} catch (Exception e) {
e.printStackTrace();
}
});
retrievalThread.start();
}
}
}
}
代理模式的变体都会将客户对主题(subject)施加的方法调用拦截下来,这种间接的级别可以让我们做很多事情。
保护代理:
package headfirst.designpatterns.proxy.javaproxy;
public interface Person {
String getName();
String getGender();
String getInterests();
int getGeekRating();
void setName(String name);
void setGender(String gender);
void setInterests(String interests);
void setGeekRating(int rating);
}
package headfirst.designpatterns.proxy.javaproxy;
public class PersonImpl implements Person {
String name;
String gender;
String interests;
int rating;
int ratingCount = 0;
public String getName() {
return name;
}
public String getGender() {
return gender;
}
public String getInterests() {
return interests;
}
public int getGeekRating() {
if (ratingCount == 0) return 0;
return (rating/ratingCount);
}
public void setName(String name) {
this.name = name;
}
public void setGender(String gender) {
this.gender = gender;
}
public void setInterests(String interests) {
this.interests = interests;
}
public void setGeekRating(int rating) {
this.rating += rating;
ratingCount++;
}
}
这里涉及到一个权限问题,自己不能给自己打分,也不能改变其他人的个人信息。
package headfirst.designpatterns.proxy.javaproxy;
import java.lang.reflect.*;
public class OwnerInvocationHandler implements InvocationHandler {
Person person;
public OwnerInvocationHandler(Person person) {
this.person = person;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws IllegalAccessException {
try {
if (method.getName().startsWith("get")) {
return method.invoke(person, args);
} else if (method.getName().equals("setGeekRating")) {
throw new IllegalAccessException();
} else if (method.getName().startsWith("set")) {
return method.invoke(person, args);
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
package headfirst.designpatterns.proxy.javaproxy;
import java.lang.reflect.*;
import java.util.*;
public class MatchMakingTestDrive {
HashMap<String, Person> datingDB = new HashMap<String, Person>();
public static void main(String[] args) {
MatchMakingTestDrive test = new MatchMakingTestDrive();
test.drive();
}
public MatchMakingTestDrive() {
initializeDatabase();
}
public void drive() {
Person joe = getPersonFromDatabase("Joe Javabean");
Person ownerProxy = getOwnerProxy(joe);
System.out.println("Name is " + ownerProxy.getName());
ownerProxy.setInterests("bowling, Go");
System.out.println("Interests set from owner proxy");
try {
ownerProxy.setGeekRating(10);
} catch (Exception e) {
System.out.println("Can't set rating from owner proxy");
}
System.out.println("Rating is " + ownerProxy.getGeekRating());
Person nonOwnerProxy = getNonOwnerProxy(joe);
System.out.println("Name is " + nonOwnerProxy.getName());
try {
nonOwnerProxy.setInterests("bowling, Go");
} catch (Exception e) {
System.out.println("Can't set interests from non owner proxy");
}
nonOwnerProxy.setGeekRating(3);
System.out.println("Rating set from non owner proxy");
System.out.println("Rating is " + nonOwnerProxy.getGeekRating());
}
Person getOwnerProxy(Person person) {
return (Person) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
}
Person getNonOwnerProxy(Person person) {
return (Person) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new NonOwnerInvocationHandler(person));
}
Person getPersonFromDatabase(String name) {
return (Person)datingDB.get(name);
}
void initializeDatabase() {
Person joe = new PersonImpl();
joe.setName("Joe Javabean");
joe.setInterests("cars, computers, music");
joe.setGeekRating(7);
datingDB.put(joe.getName(), joe);
Person kelly = new PersonImpl();
kelly.setName("Kelly Klosure");
kelly.setInterests("ebay, movies, music");
kelly.setGeekRating(6);
datingDB.put(kelly.getName(), kelly);
}
}
----------------------------