一、概述
给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问。代理对象在客户端对象和目标对象之间起到中介的作用,它去掉客户不能看到的内容和服务或者增添客户需要的额外的新的服务。
二、结构与实现
- 结构
(1)Subject(抽象主题角色):
声明了RealSubject和Proxy的共同接口,这样一来,在任何使用RealSubject的地方都可以使用Proxy。
(2)Proxy(代理主题对象):
包含对RealSubject的引用,从而可以在任何时候操作RealSubject,通常,在Proxy中在不仅仅单纯调用真实主题对象中的操作。
(3)RealSubject(真实主题角色):
定义了代理主题对象所代表的真实对象,实现了真实的业务操作。
- 实现
abstract class Subject{
public abstract void request();
}
class RealSubject extends Subject{
public abstract void request(){
//真实的业务操作
}
}
class Proxy extends Subject{
private RealSubject real=new RealSubject();
public abstract void request(){
beforeRequest();
real.request();
afterRequest();
}
public void beforeRequest(){
}
public void afterRequest(){
}
}
三、应用实例
- 分析:
增加的新功能为身份验证,查询日志。这两个方法可放添加到代理对象中,而无需直接对原有的商务信息查询对象进行修改。
-
类图:
-
代码实现
//身份验证和日志记录类
class Access {
public boolean isAccess(String userId){
if(userId.equals("jack")){
System.out.println("用户:"+userId+"登录成功!");
return true;
}else {
System.out.println("用户"+userId+"不存在,登录失败!");
return false;
}
}
}
class Log{
public void writeLog(String userId){
System.out.println("用户"+userId+"查询次数加1");
}
}
//抽象查询类
interface Searcher{
public abstract String doSearch(String userId,String keyword);
}
//真实查询类
class RealSearcher implements Searcher{
@Override
public String doSearch(String userId, String keyword) {
System.out.println("用户:"+userId+",使用关键词:"+keyword+"查询商务信息!");
return "返回具体内容";
}
}
//代理类
class ProxySearcher implements Searcher {
//关联真实查询类,身份验证类,日志记录类
private RealSearcher real=new RealSearcher();
private Access access;
private Log log;
//增强后的查询功能,查询之前进行身份验证,查询之后进行日志记录。
@Override
public String doSearch(String userId, String keyword) {
if(this.isAccess(userId)){
String result = real.doSearch(userId, keyword);
this.writeLog(userId);
return result;
}else {
return null;
}
}
//将身份验证的方法和日志记录的方法进行包装
public boolean isAccess(String userId){
access=new Access();
return access.isAccess(userId);
}
public void writeLog(String userId){
log=new Log();
log.writeLog(userId);
}
}
四、扩展
- 远程代理:
(1)为一个位于不同的地址空间的对象提供一个本地的代理对象。
(2)使客户端可以访问在远程主机上的对象,远程主机可能具有更好的计算性能和处理速度,可以快速响应并处理客户端的请求。
(3)远程的业务对象在本地主机中有一个代理对象,该代理对象负责对远程业务的访问和网络通信,它对于客户端而言是透明的。客户端无需关心实现的具体业务的是谁,只需要按照服务接口所定义的方式直接与本地主机中的代理对象交互即可。
- 虚拟代理:
(1)一个对象需要较长的加载时间或一个对象的加载十分耗费资源时适用虚拟代理。
(2)虚拟代理即创建一个消耗相对较小的对象来表示,真实对象只有在需要时才会被真正创建。
- JDK动态代理:
(1)静态代理:在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或是继承相同的父类。即代理类所实现的接口和所代理的方法都被固定。
(2)动态代理:让系统在运行时根据实际需要来动态创建代理类,让同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法。
(3)JDK动态代理的代理对象不需要实现接口,但目标对象要实现接口,代理对象的生成是利用了JDK的API,动态地在内存中构建代理对象。
- ITeacherDao为真实目标对象TeacherDao的实现接口。
- ProxyFactory利用JDK动态代理,动态生成代理类。
class ProxyFactory{
//维护一个目标对象
private Object target;
public ProxyFactory(Object target){
this.target=target;
}
//给目标对象生成一个代理对象
//public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
// InvocationHandler h)
// loader:目标对象的类加载器
// interface:目标对象实现的接口类型
// h:事件处理,执行目标对象的方法时,会触发事件处理方法。
//InvocationHandler匿名内部类,对invoke方法进行重写。
public Object getProxyInstance(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new
InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("jdk代理开始...");
//调用目标对象的方法
Object invokeVal = method.invoke(target, args);
System.out.println("jdk代理结束....");
return invokeVal;
}
});
}
}
- Cglib动态代理(也叫子类代理):
(1)静态代理和JDK动态代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理。
(2)它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。
(3)AOP编程如何选择代理模式:需要实现接口,用JDK代理,不需要实现接口,用Cglib代理。
五、总结
- 对象的间接访问,提供一个代理对象直接与客户端进行交流,执行代理对象的方法时,底层执行的是目标对象的方法。
- 主要角色:抽象目标类,目标对象类(真实对象类),代理类。
- 适用:访问远程主机对象;当耗费资源较少的对象需要代表耗费资源较多的对象;当被频繁访问的操作结果提供一个临时存储空间;需要控制一个对象的访问为不同用户提供不同级别的访问权限时。