一. 前言

1. 定义

Proxy Pattern:给目标对象提供一个代理对象,并由代理对象控制对目标对象的访问。

二. 静态代理

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
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
/**
* 静态代理
*/
public class StaticProxy {
/**
* 抽象的需求类
*/
public interface Person {
//买东西
void buy();
}

/**
* 真实的需求类
*/
public static class Demand implements Person {

String thing;

public Demand(String thing) {
this.thing = thing;
}

@Override
public void buy() {
System.out.println("被代理者-需求者:我想买"+thing);
}
}

/**
* 代理类
*/
public static class Purchase implements Person {
Person person;

public Purchase(Person person) {
this.person = person;
}

@Override
public void buy() {
System.out.println("代理者-代购:你想买什么东西");

//调用被代理者自己的buy方法
person.buy();
}
}

/**
* 测试
*/
public static void main(String[] args) {
//需求者
Demand demand = new Demand("口红");

//代理者
Purchase purchase = new Purchase(demand);
purchase.buy();
}
}
1
2
3
//运行结果
代理者-代购:你想买什么东西
被代理者-需求者:我想买口红

三. 动态代理

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
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
/**
* 动态代理
*/
public class DynamicProxy {
/**
* 抽象的需求类
*/
public interface Person {
//买东西
void buy();
}

/**
* 真实的需求类
*/
public static class Demand implements Person {
String name;
String thing;

public Demand(String name, String thing) {
this.name = name;
this.thing = thing;
}

@Override
public void buy() {
System.out.println("被代理者-"+name+":我想买"+thing);
}
}

/**
* 代理类
*/
public static class Purchase implements InvocationHandler {
//绑定被代理的对象
Object object;

public Object newPurchase(Object object) {
//存储
this.object = object;

//返回实例对象
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
this
);
}

@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
System.out.println("代理者-代购:你想买什么东西");

//调用被代理者对象的方法
return method.invoke(object, objects);
}
}

/**
* 测试
*/
public static void main(String[] args) {
//需求者
Demand demand1 = new Demand("张三", "口红");
Demand demand2 = new Demand("李四", "面膜");

//动态代理者
Purchase purchase = new Purchase();

//获取代理类
Person person1 = (Person) purchase.newPurchase(demand1);
Person person2 = (Person) purchase.newPurchase(demand2);

//代理购买
person1.buy();
person2.buy();
}
}
1
2
3
4
5
//运行结果
代理者-代购:你想买什么东西
被代理者-张三:我想买口红
代理者-代购:你想买什么东西
被代理者-李四:我想买面膜

3. 关于代码中一些接口或方法的理解

① InvocationHandler接口

InvocationHandler接口是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。

每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会被转发到实现InvocationHandler接口类的invoke方法来调用,看如下invoke方法:

1
2
3
4
5
6
7
8
/**
* proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
* method:我们所要调用某个对象真实的方法的Method对象
* args:指代代理对象方法传递的参数
*/
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

② Proxy类

1
2
3
4
5
6
/**
* loader:一个classloader对象,定义了由哪个classloader对象对生成的代理类进行加载
* interfaces:一个interface对象数组,表示我们将要给我们的代理对象提供一组什么样的接口,如果我们提供了这样一个接口对象数组,那么也就是声明了代理类实现了这些接口,代理类就可以调用接口中声明的所有方法。
* 一个InvocationHandler对象,表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上,并最终由其调用。
*/
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler i) throws IllegalArgumentException

③ 参考

Java动态代理InvocationHandler和Proxy学习笔记

四. 系统中的例子

Android的源码中多个地方都用到代理模式,比如ActivityManagerProxy这个代理类。

五. 静态代理和动态代理的区别

1. 静态代理的缺点

  • 静态代理如果接口新增一个方法,除了所有实现类(真实主题类)需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

  • 代理对象只服务于一种类型的对象,如果要服务多类型的对象。必须要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

2. 动态代理的优点

  • 可以通过一个代理类完成全部的代理功能,接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。当接口方法数量较多时,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
  • 动态代理的应用使我们的类职责更加单一,复用性更强。

3. 动态代理的缺点

不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,那么就不能使用这种方式进行动态代理(因为$Proxy()这个类集成了Proxy,Java的集成不允许出现多个父类)。

六. 优缺点以及应用场景

1. 优点

  • 代理作为调用者和真实主题的中间层,降低了模块间和系统的耦合性。
  • 可以以一个小对象代理一个大对象,达到优化系统提高运行速度的目的。
  • 代理对象能够控制调用者的访问权限,起到了保护真实主题的作用。

2. 缺点

  • 由于在调用者和真实主题之间增加了代理对象,因此可能会造成请求的处理速度变慢。
  • 实现代理模式需要额外的工作(有些代理模式的实现非常复杂),从而增加了系统实现的复杂度。

3. 应用场景

当一个对象不能或者不想直接访问另一个对象时,可以通过一个代理对象来间接访问。为保证客户端使用的透明性,委托对象和代理对象要实现同样的接口。

被访问的对象不想暴露全部内容时,可以通过代理去掉不想被访问的内容。

根据适用范围,代理模式可以分为以下几种:

  • 远程代理:为一个对象在不同的地址空间提供局部代表,这样系统可以将Server部分的事项隐藏。
  • 虚拟代理:如果要创建一个资源消耗较大的对象,可以先用一个代理对象表示,在真正需要的时候才真正创建。
  • 保护代理:用代理对象控制对一个对象的访问,给不同的用户提供不同的访问权限。
  • 智能引用:在引用原始对象的时候附加额外操作,并对指向原始对象的引用增加引用计数。

参考文章

代理模式(Proxy Pattern):静态代理 - 最易懂的设计模式解析

代理模式(Proxy Pattern):动态代理 - 最易懂的设计模式解析

Android的设计模式-代理模式