代理模式
# 一、概述
代理(Proxy)模式为对象提供一个替身,以控制这个对象的访问。这样做的好处是可以在目标对象实现的基础上,增强额外的功能操作。
代理中有两个核心角色:代理对象和被代理对象。
代理的应用场景:
- 安全代理:屏蔽对真实角色的直接访问;
- 远程代理:通过代理类处理远程方法调用;
- 延迟加载:先加载轻量级的代理对象,真正需要的时候再加载真实对象。
# 1.1 解决了什么问题
有句话叫做专业的事情交给专业的人做,在生活中有很多需求是需要非常高的专业性的,比如买房、买车、案件诉讼等。这些需求自己可以做,但是因为专业性比较高,学习成本也比较高或者自己不想花太多时间在这些事情上面。因为在处理这些需求之前可能需要做大量的调研准备工作。
# 1.2 解决方案
寻找第三方代理,让代理对象替自己完成那些专业人元需要完成的工作。如买房找房产中介、买车找中间车商、案件诉讼找律师等。
# 二、实现方式
在 Java 生态中,代理的实现方式有三种:
- 静态代理
- JDK 动态代理
- CGLIB 动态代理
静态代理指的是代理类和被代理类在编译期间就已经显式确定,静态代理不利于程序的扩展。
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
其中静态代理和 JDK 动态代理是基于接口实现的,通过原生的 JDK 就可以实现。CGLIB 代理是借助于第三方库实现的,可以在程序运行期间动态创建对象,而不需要实现任何接口。
# 2.1 静态代理
package com.sqlboy.reflect;
public class StaticProxyTest {
public static void main(String[] args) {
// 被代理对象
Customer customer1 = new Customer("找房子");
// 代理对象
HouseWorker houseWorker = new HouseWorker(customer1);
// 代理对象通过自己的方法,间接调用了被代理对象的方法
houseWorker.action();
// 被代理对象
Customer customer2 = new Customer("买车");
// 代理对象
CarWorker carWorker = new CarWorker(customer2);
// 代理对象通过自己的方法,间接调用了被代理对象的方法
carWorker.action();
}
}
/**
* 抽象代理行为
*/
interface Worker {
void action();
}
/**
* 代理对象
*/
class HouseWorker implements Worker {
// 被代理对象
Customer customer;
HouseWorker(Customer customer) {
this.customer = customer;
}
@Override
public void action() {
customer.action();
System.out.println("我是房屋中介,我帮你去完成");
}
}
class CarWorker implements Worker {
// 被代理对象
Customer customer;
CarWorker(Customer customer) {
this.customer = customer;
}
@Override
public void action() {
customer.action();
System.out.println("我是汽车销售,我替你去完成");
}
}
class Customer implements Worker {
String demand;
Customer(String demand) {
this.demand = demand;
}
@Override
public void action() {
System.out.println("我是雇主,我需要" + demand);
}
}
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
输出:
我是雇主,我需要找房子
我是房屋中介,我帮你去完成
我是雇主,我需要买车
我是汽车销售,我替你去完成
2
3
4
静态代理的特点是在程序编译期间就需要指定代理对象和被代理对象,可以看到如果有不同的需求,就需要定义不同的代理类和代理对象,不利于扩展。
# 2.2 JDK 动态代理
动态代理的优势是抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。使用动态代理的核心步骤是:
- 根据加载的被代理类动态地创建一个代理类的对象;
- 调用代理类的某个方法时,动态地调用被代理类中的同名方法。
Java 中提供的 Proxy 类用来专门完成动态代理操作,它是所有动态代理类的父类。Proxy 中的核心方法:
static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces)
:创建 一个动态代理类所对应的 Class 对象。static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
:直接创建一个动态代理对象。
示例:
package com.sqlboy.reflect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynamicProxy {
public static void main(String[] args) {
// 被代理对象1需要找房
Customer customer1 = new Customer("找房子");
// 代理工厂动态生成代理对象,替客户完成
Worker proxyInstance1 = (Worker) ProxyFactory.getProxyInstance(customer1);
// 代理对象通过调用自己的方法,间接调用了被代理对象的方法,知道了对方的需求
proxyInstance1.action();
Customer customer2 = new Customer("买车");
Worker proxyInstance2 = (Worker) ProxyFactory.getProxyInstance(customer2);
proxyInstance2.action();
}
}
/**
* 代理工厂
*/
class ProxyFactory {
/**
* 获取代理对象实例
*
* @param obj 被代理对象
* @return
*/
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler();
handler.bind(obj);
// 根据加载的被代理类动态地创建一个代理类的对象
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
class MyInvocationHandler implements InvocationHandler {
// 被代理对象
private Object obj;
// 为被代理对象赋值
public void bind(Object obj) {
this.obj = obj;
}
// 当通过代理对象调用某个方法a时,就会自动调用此方法
// 所以可以将被代理对象要执行的功能a定义在此方法中
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// method为代理类对象的方法
Object invokeResult = method.invoke(obj, args);
System.out.println("我是代理对象,我替你去完成");
return invokeResult;
}
}
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
输出:
我是雇主,我需要找房子
我是代理对象,我替你去完成
我是雇主,我需要买车
我是代理对象,我替你去完成
2
3
4
在上面的动态代理示例中,并没有明确定义代理对象是汽车销售还是房产中介,而是通过代理工程根据客户种类动态生成的,如果有新的客户需求(如招聘、理财等),代理工厂依然可以动态创建一个代理对象出来。
# 2.3 CGLIB 动态代理
CGLIB(Code Generation Library)是一个强大的代码生成库,可以在程序运行期间动态地扩展类和接口。
前面两种代理方式都需要代理对象和被代理对象实现一个接口,但有时候可能被代理对象并未实现任何接口,此时可以使用被代理对象的子类来实现代理,所以 CGLIB 代理也称为子类代理,它的原理是构建一个子类对象从而实现对目标对象的功能扩展。
// TODO:补充代码
# 三、源码中的应用
- java.lang.reflect.Proxy
- Spring AOP