文章目录
一、代理模式简介
Proxy模式又叫做代理模式,是构造型的设计模式之一,它可以为其他对象提供一种代理(Proxy)以控制对这个对象的访问。
所谓代理,是指具有与代理元(被代理的对象)具有相同的接口的类,客户端必须通过代理与被代理的目标类交互,而代理一般在交互的过程中(交互前后),进行某些特别的处理。
二、代理模式的结构
传统定义:
JAVA Api代理:
三、代理模式的角色与职责
- subject(抽象主题角色): 真实主题与代理主题的共同接口。
- RealSubject(真实主题角色): 定义了代理角色所代表的真实对象。
- Proxy(代理主题角色): 含有对真实主题角色的引用,代理角色通常在将客户端调用传递给真是主题对象之前或者之后执行某些操作,而不是单纯返回真实的对象。
四、代理模式的具体实现
这里主要介绍使用动态代理来创建保护代理。
最大的婚恋交友网站世纪浪园邀请我们为他们的交友资料配对进行程序设计。
应网站编辑的要求,对于每个人都有一个资料卡片,自己可以设置自己的姓名、年龄、兴趣,但是不能给自己评分;别人可以获取姓名年龄爱好,并且可以为你评分。
首先看一下资料显示:
// An highlighted block
package design.proxy.gys.protect;
public interface PersonBean {
String getName();
String getGender();
String Interests();
double getHotRating();
void setName(String name);
void setGender(String gender);
void setInterests(String interests);
void setHotRating(double rating);
}
1、不使用代理模式
所有的方法都为public访问权限,如果直接设置访问,那么所有人都能访问资料的每项内容并进行修改,这里不再写出代码。
2、使用代理模式
因为JAVA已经为我们创建了Proxy类,我们需要将代码放在InvocationHandler中,InvocationHandler的工作是代理的调用,可以将其想象成代理受到方法调用之后请求做实际工作的对象。
现在我们来实现具体的Person类:
// An highlighted block
package design.proxy.gys.protect;
public class Person implements PersonBean{
String name;
String gender;
String interests;
int rating;
int ratingcount=0;
public Person() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String getName() {
// TODO Auto-generated method stub
return name;
}
@Override
public String getGender() {
// TODO Auto-generated method stub
return gender;
}
@Override
public String Interests() {
// TODO Auto-generated method stub
return interests;
}
@Override
public double getHotRating() {
// TODO Auto-generated method stub
if(ratingcount==0) return 0;
return rating/ratingcount;
}
@Override
public void setName(String name) {
// TODO Auto-generated method stub
this.name=name;
}
@Override
public void setGender(String gender) {
// TODO Auto-generated method stub
this.gender=gender;
}
@Override
public void setInterests(String interests) {
// TODO Auto-generated method stub
this.interests=interests;
}
@Override
public void setHotRating(double rating) {
// TODO Auto-generated method stub
this.rating+=rating;
ratingcount++;
}
}
第一步创建合适的InvocationHandler
在这里我们需要两个InvocationHandler类。一个是OwnHandler,用于给资料的拥有者使用,另一个是NonOwnHandler,用于给非拥有者使用。
当代理的方法被调用时,代理就会把这个调用转发给InvocationHandler。让我们看看这是如何让做到的。
首先看一下InvocationHandler接口:
这里只有一个invoke()方法,不管代理被调用的是什么方法,处理器被调用的一定是invoke()方法。让我们看看如何工作的。
假设Proxy调用的是setHotRating()方法,proxy接着会调用InvocationHandler的invoke()方法。
handler决定要如何处置这个请求。接下来我们就能看到,在OwnHandler中,拥有者能进行任何全县的动作,除了给自己设置评分;在中,只能获取名字性别爱好和设置评分,没有任何其他的权限。
OwnHandler:
// An highlighted block
package design.proxy.gys.protect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class OwnHandler implements InvocationHandler{
PersonBean person;
public OwnHandler(PersonBean person) {
super();
this.person = person;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
// TODO Auto-generated method stub
try {
if(method.getName().startsWith("get"))
return method.invoke(person, args);
else if(method.getName().equals("setHotRating"))
throw new IllegalAccessException();
else if(method.getName().startsWith("set"))
return method.invoke(person, args);
}
catch(InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
NonOwnHandler:
// An highlighted block
package design.proxy.gys.protect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class NonOwnHandler implements InvocationHandler{
PersonBean person;
public NonOwnHandler(PersonBean person) {
super();
this.person = person;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException {
// TODO Auto-generated method stub
try {
if(method.getName().startsWith("get"))
return method.invoke(person, args);
else if(method.getName().equals("setHotRating"))
return method.invoke(person, args);
else if(method.getName().startsWith("set"))
throw new IllegalAccessException();
}
catch(InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
将Person传入handler的构造器,proxy方法被调用时就会调用invoke()方法,handler根据是否是资料的拥有着进行权限访问控制。
第二步创建动态代理类
这些代码用来创建代理的实例类。将代码放在Test类的静态方法中。
获取拥有者的代理:
// An highlighted block
static PersonBean getOwnProxy(PersonBean person) {
return (PersonBean)Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new OwnHandler(person)
);
}
获取非拥有者的代理:
// An highlighted block
static PersonBean getNonOwnProxy(PersonBean person) {
return (PersonBean)Proxy.newProxyInstance(
person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
new NonOwnHandler(person)
);
}
好了,我们该给网站进行测试了,创建一个资料,分别通过OwnHandler和NonOwnHandler两个代理进行资料的修改:
// An highlighted block
package design.proxy.gys.protect;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
PersonBean p=new Person();
PersonBean owner=getOwnProxy(p);
owner.setName("大傻子");
owner.setInterests("跑步");
owner.setGender("男");
try {
owner.setHotRating(4);}
catch(Exception e) {
System.out.println("不能给自己设置评分");
}
printPerson(p);
PersonBean other=getNonOwnProxy(p);
try {
other.setGender("女");
}
catch(Exception e) {
System.out.println("不能给别人设置性别");
}
try {
other.setInterests("跳舞");
}
catch(Exception e) {
System.out.println("不能给别人设置爱好");
}
other.setHotRating(3.0);
printPerson(p);
}
static PersonBean getOwnProxy(PersonBean person) {
return (PersonBean)Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new OwnHandler(person));
}
static PersonBean getNonOwnProxy(PersonBean person) {
return (PersonBean)Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new NonOwnHandler(person));
}
static void printPerson(PersonBean person) {
System.out.print("姓名 "+person.getName());
System.out.print("+性别 "+person.getGender());
System.out.print("+爱好 "+person.Interests());
System.out.println("+评分 "+person.getHotRating());
}
}
测试结果如下:
// An highlighted block
不能给自己设置评分
姓名 大傻子+性别 男+爱好 跑步+评分 0.0
不能给别人设置性别
不能给别人设置爱好
姓名 大傻子+性别 男+爱好 跑步+评分 3.0
世纪浪园网站的总裁很满意,毕竟我们满足了他们的要求。
五、代理模式的种类
代理的主要包括常规的模式:
- 虚拟代理:为创建开销大的对象提供代理服务,例如网络下的图片缓存。
- 动态代理:运行时i动态的创建代理对象,将方法调用转发到指定类。
- 保护代理:保护对象的接口,进行访问控制。
以及各种变体: - 防火墙代理
- 缓存代理
- 只能引用代理
- 同步代理
- 写入是复制代理