终于……看到了……代理模式……
我觉得……
这个模式……
真的……
好难啊……
首先是远程代理
Java RMI 概观
↑都是这个东西好难
代理模式:为另一个对象提供一个替身或占位符以控制对这个对象的访问。
使用代理模式创建代表(representative)对象,让代表对象控制某对象的访问,被代理的对象可以是远程的对象,创建开销大的对象或者需要安全控制的对象
类图:

RealSubject是真正做事的对象,它是被Proxy代理和控制访问的对象。客户与RealSubject的交互都必须通过Proxy。
远程代理:远程代理可以作为;另一个JVM上的对象的本地代表。调用代理方法,会代理利用网络转发到远程执行,并将结果通过网络返回给代理,再由代理将结果转给客户。
虚拟代理:虚拟代理作为创建开销大的对象的代表。虚拟代理经常直到我们真正需要一个对象的时候才创建它。当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。
栗子:
写一个程序展示CD封面,当封面没有加载好的时候,就显示“加载中。。。”
创建一个代理类,当照片没有加载好的时候,就显示加载中,但加载好之后,就好所有方法委托给真正的照片类。
为什么要虚拟代理呢?因为没加载好的时候,还没有图片,这个代理就是一个虚拟的图片,,,
import java.awt.Component;
import java.awt.Graphics;
import java.net.URL;
import javax.swing.Icon;
import javax.swing.ImageIcon;
// 代理Icon也要实现Icon接口
public class ImageProxy implements Icon {
ImageIcon imageIcon;
URL imageURL;
Thread retrievalThread;
boolean retrieving = false;
// URL是图片资源位置
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;
}
}
public void paintIcon(final Component c, Graphics g, int x, int y) {
if (imageIcon != null) {
// 如果已经有Icon 就告诉它画出自己
imageIcon.paintIcon(c, g, x, y);
} else {
// 否则就显示加载中
g.drawString("Loading CD cover, please wait...", x+300, y+190);
if (!retrieving) {
// 如果我们还有试着取出图像 那么就开始取图像
retrieving = true;
retrievalThread = new Thread(new Runnable() {
public void run() {
try {
// 要在这里加载真正的Icon图像
// 请注意 加载图像和ImageIcon是同步的 也就是说 只有在加载完成之后 ImageIcon构造器才会返回
// 这样 我们的程序就会耗在这里 动弹不得 也没办法显示消息
// 我们不希望挂起整个用户界面 所以用另一个线程取出图像
// 在线程中 我们实例化此Icon对象 其构造器会在图像加载完成后才返回
// 当图像准备好时 我们告诉Swing要重绘
imageIcon = new ImageIcon(imageURL, "CD cover");
c.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
});
retrievalThread.start();
}
}
}
}
然后是显示图片的ImageComponent类
import java.awt.*;
import javax.swing.*;
class ImageComponent extends JComponent {
private Icon icon;
public ImageComponent(Icon icon) {
this.icon = icon;
}
public void setIcon(Icon icon) {
this.icon = icon;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
int w = icon.getIconWidth();
int h = icon.getIconHeight();
int x = (800 - w)/2;
int y = (600 - h)/2;
icon.paintIcon(this, g, x, y);
}
}
测试类:
import java.net.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.util.Map.Entry;
public class ImageProxyTestDrive {
ImageComponent imageComponent;
JFrame frame = new JFrame("CD封面加载器");
JMenuBar menuBar; // 菜单栏
JMenu menu; // 菜单
Map<String, String> cds = new HashMap<>();
public static void main (String[] args) throws Exception {
new ImageProxyTestDrive();
}
public ImageProxyTestDrive() throws Exception{
// 构造菜单项用的, key=CD名, value=URL
cds.put("轨迹","http://images.cnblogs.com/cnblogs_com/wenruo/873448/o_%E8%BD%A8%E8%BF%B9.png");
cds.put("分我一半的眼泪","http://images.cnblogs.com/cnblogs_com/wenruo/873448/o_%E5%88%86%E6%88%91%E4%B8%80%E5%8D%8A%E7%9A%84%E7%9C%BC%E6%B3%AA.jpg");
cds.put("光荣","http://images.cnblogs.com/cnblogs_com/wenruo/873448/o_%E5%85%89%E8%8D%A3.png");
cds.put("画中仙","http://images.cnblogs.com/cnblogs_com/wenruo/873448/o_%E7%94%BB%E4%B8%AD%E4%BB%99.jpg");
// 设置初始的CD封面
URL initialURL = new URL(cds.get("轨迹"));
// 建立菜单栏
menuBar = new JMenuBar();
menu = new JMenu("Favorite CDs");
menuBar.add(menu);
frame.setJMenuBar(menuBar);
for(Entry<String, String> e : cds.entrySet()) {
String name = e.getKey();
String url = e.getValue();
JMenuItem menuItem = new JMenuItem(name);
menu.add(menuItem);
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
try {
imageComponent.setIcon(new ImageProxy(new URL(url)));
} catch (MalformedURLException e) {
e.printStackTrace();
}
frame.repaint();
}
});
}
// set up frame and menus
Icon icon = new ImageProxy(initialURL);
imageComponent = new ImageComponent(icon);
frame.getContentPane().add(imageComponent);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800,600);
frame.setVisible(true);
}
}
一个虚拟代理就完成了。。。
加载前:
加载后:
在上面的例子中,每一次都是创建新的ImageProxy来取得对象,即使图像已经被取出来过,可以把已加载的图片放入缓存中,这就是缓存代理。
缓存代理(Caching Proxy)会维护之前创建的对象,当收到请求时,在可能的情况下返回缓存对象。
动态代理:Java在java.lang.reflect包中有自己的代理支持,利用这个包你可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类。因为实际的代理类是在运行时被创建的,我们称这个技术为:动态代理。
动态代理类图:

动态代理的实现
有一个系统,存有每个人的信息,和每个人的评分。现在希望每个人不能给自己评分,而更改个人信息只有自己能做到。
于是设计两个代理,一个控制访问自己的类,一个控制访问其他人。
首先是个人信息的接口:
public interface PersonBean {
String getName();
String getGender();
String getInterests();
int getHotOrNotRating();
void setName(String name);
void setGender(String gender);
void setInterests(String interests);
void setHotOrNotRating(int rating);
}
具体实现类
public class PersonBeanImpl implements PersonBean {
String name;
String gender;
String interests;
int rating;
int ratingCount = 0;
public PersonBeanImpl(String name, String gender, String interests) {
super();
this.name = name;
this.gender = gender;
this.interests = interests;
}
public String getName() {
return name;
}
public String getGender() {
return gender;
}
public String getInterests() {
return interests;
}
public int getHotOrNotRating() {
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 setHotOrNotRating(int rating) {
this.rating += rating;
ratingCount++;
}
}
创建两个InvocationHandler,一个给拥有者使用
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class OwnerInvocationHandler implements InvocationHandler {
PersonBean person;
public OwnerInvocationHandler(PersonBean person) {
this.person = person;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (method.getName().startsWith("get")) {
return method.invoke(person, args);
} else if (method.getName().equals("setHotOrNotRating")) {
throw new IllegalAccessException();
} else if (method.getName().startsWith("set")) {
return method.invoke(person, args);
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
一个给非拥有者使用
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class NotOwnerInvocationHandler implements InvocationHandler {
PersonBean person;
public NotOwnerInvocationHandler(PersonBean person) {
this.person = person;
}
@Override
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("setHotOrNotRating")) {
return method.invoke(person, args);
} else if (method.getName().startsWith("set")) {
throw new IllegalAccessException();
}
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
测试类:
import java.lang.reflect.Proxy;
import java.util.ArrayList;
public class MatchMakingTestDrive {
public static void main(String[] args) {
MatchMakingTestDrive test = new MatchMakingTestDrive();
test.drive();
}
public MatchMakingTestDrive() {
initializeDatabsae();
}
public void drive() {
PersonBean joe = getPersonFromDatabase("Joe JavaBean");
PersonBean ownerProxy = getOwnerProxy(joe);
System.out.println("Name is " + ownerProxy.getName());
ownerProxy.setInterests("bowling, Go");
System.out.println("Interests set from owner proxy");
try {
ownerProxy.setHotOrNotRating(10);//不能给自己平分
} catch (Exception e) {
System.out.println("Can't set rating from owner proxy");
}
System.out.println("Rating is " + ownerProxy.getHotOrNotRating());
PersonBean 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.setHotOrNotRating(3);
System.out.println("Rating set from non owner proxy");
System.out.println("Rating is " + nonOwnerProxy.getHotOrNotRating());
}
PersonBean getOwnerProxy(PersonBean person) {
return (PersonBean) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnerInvocationHandler(person));
}
PersonBean getNonOwnerProxy(PersonBean person) {
return (PersonBean) Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new NotOwnerInvocationHandler(person));
}
/************假装这是数据库实现**************/
ArrayList<PersonBean> beans;
void initializeDatabsae() {
beans = new ArrayList<>();
beans.add(new PersonBeanImpl("Joe JavaBean", "男", "学习"));
}
PersonBean getPersonFromDatabase(String name) {
for (int i = 0; i < beans.size(); ++i) {
PersonBean person = beans.get(i);
if (person.getName().equals(name)) return person;
}
return null;
}
}
一些其他的代理模式:
防火墙代理:控制网络资源的访问,保护主题免于“坏客户”的侵害。
智能引用代理:当主题被引用时,进行额外的动作,例如计算一个对象呗引用的次数。
缓存代理:为开销大的运算结果提供暂时的存储,它也允许多个客户共享结果,以减少计算或网络延迟。
同步代理:在多线程的情况下为主题提供安全的访问。
复杂隐藏代理:用来隐藏一个类的复杂集合的复杂度,并进行访问控制。
写入时复制代理:用来控制对象的复制,方法是延迟对象的复制,直到客户真的需要为止。这是虚拟代理的变体。
来源:https://www.cnblogs.com/wenruo/p/6556283.html