代理模式-看这一篇就够了

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式。

在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

介绍

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

如何解决:增加中间层。

关键代码:实现与被代理类组合。

应用实例:

  • 1、Windows 里面的快捷方式。
  • 2、猪八戒去找高翠兰结果是孙悟空变的,可以这样理解:把高翠兰的外貌抽象出来,高翠兰本人和孙悟空都实现了这个接口,猪八戒访问高翠兰的时候看不出来这个是孙悟空,所以说孙悟空是高翠兰代理类。
  • 3、买火车票不一定在火车站买,也可以去代售点。
  • 4、一张支票或银行存单是账户中资金的代理。支票在市场交易中用来代替现金,并提供对签发人账号上资金的控制。
  • 5、spring aop。

优点:

  • 1、职责清晰。
  • 2、高扩展性。
  • 3、智能化。

缺点:

  • 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
  • 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

使用场景:按职责来划分,通常有以下使用场景:

  • 1、远程代理。
  • 2、虚拟代理。
  • 3、Copy-on-Write 代理。
  • 4、保护(Protect or Access)代理。
  • 5、Cache代理。 6、防火墙(Firewall)代理。
  • 7、同步化(Synchronization)代理。
  • 8、智能引用(Smart Reference)代理。

注意事项: 1、和适配器模式的区别:适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。 2、和装饰器模式的区别:装饰器模式为了增强功能,而代理模式是为了加以控制。

实现

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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;


public class ConnectionProxy implements InvocationHandler{

private Connection connection;

public ConnectionProxy(Connection connection) {
super();
this.connection = connection;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里判断是Connection接口的close方法的话
if (Connection.class.isAssignableFrom(proxy.getClass()) && method.getName().equals("close")) {
//我们不执行真正的close方法
//method.invoke(connection, args);
//将连接归还连接池
DataSource.getInstance().recoveryConnection(connection);
return null;
}else {
return method.invoke(connection, args);
}
}

public Connection getConnectionProxy(){
return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
}

}
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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{

private Object source;

public DynamicProxy(Object source) {
super();
this.source = source;
}

public void before(){
System.out.println("在方法前做一些事,比如打开事务");
}

public void after(){
System.out.println("在方法返回前做一些事,比如提交事务");
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//假设我们切入toString方法,其他其实也是类似的,一般我们这里大部分是针对特定的方法做事情的,通常不会对类的全部方法切入
//比如我们常用的事务管理器,我们通常配置的就是对save,update,delete等方法才打开事务
if (method.getName().equals("toString")) {
before();
}
Object result = method.invoke(source, args);
if (method.getName().equals("toString")) {
after();
}
return result;
}

public Object getProxy(){
return Proxy.newProxyInstance(getClass().getClassLoader(), source.getClass().getInterfaces(), this);
}


}
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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{

private Object source;

public DynamicProxy(Object source) {
super();
this.source = source;
}

public void before(){
System.out.println("在方法前做一些事,比如打开事务");
}

public void after(){
System.out.println("在方法返回前做一些事,比如提交事务");
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//假设我们切入toString方法,其他其实也是类似的,一般我们这里大部分是针对特定的方法做事情的,通常不会对类的全部方法切入
//比如我们常用的事务管理器,我们通常配置的就是对save,update,delete等方法才打开事务
if (method.getName().equals("toString")) {
before();
}
Object result = method.invoke(source, args);
if (method.getName().equals("toString")) {
after();
}
return result;
}

public Object getProxy(){
return Proxy.newProxyInstance(getClass().getClassLoader(), source.getClass().getInterfaces(), this);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TestClass{

public void method1() {
System.out.println("TestClass.method1");
}

public void method2() {
System.out.println("TestClass.method2");
}

public void method3() {
System.out.println("TestClass.method3");
}
}
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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{

private Object source;

public DynamicProxy(Object source) {
super();
this.source = source;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("接口的方法全部变成这样了");
//这里source是TestClass,但是我们不能使用反射调用它的方法,像下面这样,放开这一行会抛异常
//return method.invoke(source, args);
return null;
}

public static void main(String[] args) {
//只要你传入就可以强转成功
TestInterface object = (TestInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{TestInterface.class}, new DynamicProxy(new TestClass()));
object.method1();
object.method2();
object.method3();
}
}
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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{

private Object source;

public DynamicProxy(Object source) {
super();
this.source = source;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("接口的方法全部变成这样了");
//这里source是TestClass,但是我们不能使用反射调用它的方法,像下面这样,放开这一行会抛异常
//return method.invoke(source, args);
return null;
}

public static void main(String[] args) {
//只要你传入就可以强转成功
TestInterface object = (TestInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{TestInterface.class}, new DynamicProxy(new TestClass()));
object.method1();
object.method2();
object.method3();
}
}
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

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{

private Object source;

public DynamicProxy(Object source) {
super();
this.source = source;
}

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("接口的方法全部变成这样了");
//这里source是TestClass,但是我们不能使用反射调用它的方法,像下面这样,放开这一行会抛异常
//return method.invoke(source, args);
return null;
}

public static void main(String[] args) {
//只要你传入就可以强转成功
TestInterface object = (TestInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{TestInterface.class}, new DynamicProxy(new TestClass()));
object.method1();
object.method2();
object.method3();
}
}
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces) throws IllegalArgumentException {
//如果传入的接口长度大于65535就抛出异常,我去你妹。。。
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

Class proxyClass = null;

/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];

Set interfaceSet = new HashSet(); // for detecting duplicates

for (int i = 0; i < interfaces.length; i++) {
/*
* Verify that the class loader resolves the name of this interface
* to the same Class object.
*/
String interfaceName = interfaces[i].getName();
Class interfaceClass = null;
try {
//加载每一个接口的运行时Class信息
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
//如果采用你传入的类加载器载入的Class和你传入的Class不相等则抛出异常
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(interfaces[i]
+ " is not visible from class loader");
}

//如果你传入的不是接口抛出异常
/*
* Verify that the Class object actually represents an interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(interfaceClass.getName()
+ " is not an interface");
}

//如果你传入的接口重复抛异常
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException("repeated interface: "
+ interfaceClass.getName());
}
interfaceSet.add(interfaceClass);

interfaceNames[i] = interfaceName;
}

/*
* Using string representations of the proxy interfaces as keys in the
* proxy class cache (instead of their Class objects) is sufficient
* because we require the proxy interfaces to be resolvable by name
* through the supplied class loader, and it has the advantage that
* using a string representation of a class makes for an implicit weak
* reference to the class.
*/
Object key = Arrays.asList(interfaceNames);

/*
* Find or create the proxy class cache for the class loader.
*/
Map cache;
synchronized (loaderToCache) {
//这个是为了存储每一个类加载器所载入过的代理接口的代理类
cache = (Map) loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap();
loaderToCache.put(loader, cache);
}
/*
* This mapping will remain valid for the duration of this method,
* without further synchronization, because the mapping will only be
* removed if the class loader becomes unreachable.
*/
}

/*
* Look up the list of interfaces in the proxy class cache using the
* key. This lookup will result in one of three possible kinds of
* values: null, if there is currently no proxy class for the list of
* interfaces in the class loader, the pendingGenerationMarker object,
* if a proxy class for the list of interfaces is currently being
* generated, or a weak reference to a Class object, if a proxy class
* for the list of interfaces has already been generated.
*/
synchronized (cache) {
/*
* Note that we need not worry about reaping the cache for entries
* with cleared weak references because if a proxy class has been
* garbage collected, its class loader will have been garbage
* collected as well, so the entire cache will be reaped from the
* loaderToCache map.
*/
do {
//检查是否有生成好的代理
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
}
//有的话直接返回
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
//否则看一下这个代理类是不是正在构造中,是的话就在cache对象上等待
} else if (value == pendingGenerationMarker) {
// proxy class being generated: wait for it
try {
cache.wait();
} catch (InterruptedException e) {
/*
* The class generation that we are waiting for should
* take a small, bounded time, so we can safely ignore
* thread interrupts here.
*/
}
continue;
//如果没有现成的,也没有创造中的,那就开始创造代理类
} else {
/*
* No proxy class for this list of interfaces has been
* generated or is being generated, so we will go and
* generate it now. Mark it as pending generation.
*/
//将当前代理类置为正在构造中,并直接退出循环
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}

try {
String proxyPkg = null; // package to define proxy class in

//这一段是看你传入的接口中有没有不是public的接口,如果有,这些接口必须全部在一个包里定义的,否则抛异常
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that all
* non-public proxy interfaces are in the same package.
*/
for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}

if (proxyPkg == null) { // if no non-public proxy interfaces,
proxyPkg = ""; // use the unnamed package
}

{
/*
* Choose a name for the proxy class to generate.
*/
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
//生成一个随机代理类名
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Verify that the class loader hasn't already defined a class
* with the chosen name.
*/

//这一句就是重中之重了,生成代理类的class文件,这就是JDK动态代理的原理了,通过动态生成class文件来产生的代理类
//这个generateProxyClass方法下面会着重介绍
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
//得到class文件二进制流后,直接载入代理类
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other invalid
* aspect of the arguments supplied to the proxy class
* creation (such as virtual machine limitations exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
//proxyClasses这个Map是为了来判断是不是代理的Class
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);

} finally {
/*
* We must clean up the "pending generation" state of the proxy
* class cache entry somehow. If a proxy class was successfully
* generated, store it in the cache (with a weak reference);
* otherwise, remove the reserved entry. In all cases, notify all
* waiters on reserved entries in this cache.
*/
synchronized (cache) {
if (proxyClass != null) {
//最终将生成的代理用弱引用包装起来放到cache当中
cache.put(key, new WeakReference(proxyClass));
} else {
//如果代理类是空则移除代理的接口所代表的key值
cache.remove(key);
}
//通知正在等待在cache对象上的线程,告诉他们可以继续了,代理Class加载完毕了
cache.notifyAll();
}
}
return proxyClass;
}

应用

spring aop