代理模式

说明

  • 定义
    代理模式,为一个对象提供一个替身或占位符以控制对这个对象的访问。被代理的对象可以是远程的对象、创建开销大的对象、需要安全控制的对象。
  • 角色
    1. subject:真正对象与代理对象都要实现的接口,允许客户可以像处理真正对象一样处理代理对象
    2. proxy:代理对象,并持有真正对象的引用,必要时可以将请求转发给真正对象。
    3. realSubject:真正对象,代理对象控制对其访问
  • 类型
    • 远程代理:管理客户端与远程之间的交互
    • 虚拟代理:创建开销大的对象的代表。虚拟代理经常知道我们真正需要一个对象的时候才创建它,当对象在创建前和创建中时,由虚拟代理来扮演对象的替身。对象创建后,代理就会将请求直接委托给对象。
    • 动态代理
    • 保护代理:基于调用者控制对对象方法的访问
    • 防火墙代理(Firewall Proxy):控制网络资源访问,保护主题免于“坏客户”的侵害
    • 智能引用代理(Smart Reference Proxy):主题被访问时,进行额外的动作
    • 缓存代理(Caching Proxy):为开销大的运算结果提供暂时的存储,也允许多个客户共享结果,以减少计算或网络延迟。(web服务器代理,内容管理,出版系统)
    • 同步代理(Synchronization Proxy)多线程的情况下为主题提供安全的访问。(javaSpaces为分散式环境内的潜在对象集合提供同步访问控制)
    • 复杂隐藏代理(Complexity Hiding Proxy):用来隐藏一个类得复杂集合的复杂度,并进行控制访问,也称为外观代理(Facade Proxy)
    • 写入时复制代理(Copy-On-Write Proxy):用来控制对象的复制,方法是延迟对象的复制,知道客户真的需要为止。是虚拟代理的变体。(java5,CopyOnWriteArrayList)
  • 代理模式与装饰者(Decorator)模式区别
    1. 两者类图一样,但是目的不同,装饰者是为给对象增加行为,而代理模式是控制对对象的访问。
    2. 代理模式有时候会创建真实对象,装饰者模式不会创建被装饰的对象
    3. 代理不会讲一个主题包装多次,装饰者不做限制
  • 使用方式
    一般使用工厂模式,实例化并返回代理对象

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
* 代理与真正对象的共同接口
* @author sky
*
*/
public interface Subject {

public void doSomething();
}

/**
* 真正对象
* @author sky
*
*/
public class RealSubject implements Subject {

@Override
public void doSomething() {
System.out.println("真正对象的操作");
}

}

/**
* 代理类
* @author sky
*
*/
public class ProxySubject implements Subject {

private RealSubject subject;

public ProxySubject(){
subject = new RealSubject();
}

@Override
public void doSomething() {
System.out.println("代理对象添加功能");
subject.doSomething();
System.out.println("代理对象添加功能");
}

}

/**
* 测试类
* @author sky
*
*/
public class Test {

public static void main(String[] args) {
ProxySubject subject = new ProxySubject();
subject.doSomething();
}

}

输出

1
2
3
代理对象添加功能
真正对象的操作
代理对象添加功能

动态代理

JDK 5引入的动态代理机制,允许开发人员在运行时刻动态的创建出代理类及其对象。在运行时刻,可以动态创建出一个实现了多个接口的代理类。每个代理类的对象都会关联一个表示内部处理逻辑的InvocationHandler接口的实现。当使用者调用了代理对象所代理的接口中的方法的时候,这个调用的信息会被传递给InvocationHandler的invoke方法。在invoke方法的参数中可以获取到代理对象、方法对应的Method对象和调用的实际参数。invoke方法的返回值被返回给使用者。这种做法实际上相 当于对方法调用进行了拦截。
动态代理主要使用java.lang.reflect包下的InvocationHandler接口、Proxy类。InvocationHandler用来实现代理行为,Proxy用来创建代理类

动态代理代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
 /**
* 切面接口
* @author sky
*
*/
public interface AopInterface {

public void addBefore(String str);

public void addEnd(String str);
}

public class Aop implements AopInterface {

@Override
public void addBefore(String str) {
System.out.println(str+" 调用前");
}

@Override
public void addEnd(String str) {
System.out.println(str+" 调用后");
}

}

/**
* 代理行为
* @author sky
*
*/
public class AopInvocationHandler implements InvocationHandler {

private AopInterface aop;
private Object realObject;

public AopInvocationHandler(Object object){
aop = new Aop();
realObject = object;
}

@Override
public Object invoke(Object object, Method method, Object[] arg2)
throws Throwable {
// method.invoke(object, arg2); 使用object会照成死循环,不清楚有什么用
aop.addBefore(realObject.getClass().getName()+"类的"+method.getName()+"方法");
Object obj = method.invoke(realObject, arg2);
aop.addEnd(realObject.getClass().getName()+"类的"+method.getName()+"方法");
return obj;
}

}

/**
* 代理与真正对象的共同接口
* @author sky
*
*/
public interface Subject {

public void doSomething();
}

/**
* 真正对象
* @author sky
*
*/
public class RealSubject implements Subject {

@Override
public void doSomething() {
System.out.println("真正对象的操作");
}

}

/**
* 测试类
* @author sky
*
*/
public class Test {

public static void main(String[] args) {
Subject subject = new RealSubject();
Subject proxy = (Subject)Proxy.newProxyInstance(subject.getClass().getClassLoader(),
subject.getClass().getInterfaces(), new AopInvocationHandler(subject));
proxy.doSomething();
}

}

输出

1
2
3
proxy.dynamic.RealSubject类的doSomething方法 调用前
真正对象的操作
proxy.dynamic.RealSubject类的doSomething方法 调用后