设计模式——代理模式

设计模式——代理模式

模式介绍

代理模式:为其他对象提供一种代理以控制对这个对象的访问。
代理模式(Proxy)是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题.

分类

  • 静态代理:代理类是在编译时就实现好的。也就是说 Java 编译完成后代理类是一个实际的 class 文件。

  • 动态代理:代理类是在运行时生成的。也就是说 Java 编译完之后并没有实际的 class 文件,而是在运行时动态生成的类字节码,并加载到JVM中。

静态代理模式结构

模式涉及角色

RealSubject 是原对象(或称委托对象),ProxyObject是代理对象。
Subject 是委托对象和代理对象都共同实现的接口。
leave() 是委托对象和代理对象共同拥有的方法。

结构图

示例

比如某人要请假,因为有事不能直接去请假,可以找个同事(代理对象)帮他请假。

代码实现

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
/**
* ClassName: ProxyPattern <br/>
* Function: 静态代理和动态代理<br/>
*
* @author gary.liu
* @date 2017/5/23
*/
public class ProxyPattern {
public static void main(String[] args){
/**
* 静态代理测试
*/
RealSubject realSubject = new RealSubject(); //委托对象
ProxyObject proxyObject = new ProxyObject(realSubject); //代理对象
proxyObject.leave();
}
}
interface Subject{
/**
* 请假接口
*/
void leave();
}
class RealSubject implements Subject {
@Override
public void leave(){
System.out.println("RealSubject leave request");
}
}
class ProxyObject implements Subject {
private Subject subject;
public ProxyObject(Subject subject){
this.subject = subject;
}
@Override
public void leave(){
System.out.println("真正对象告诉代理帮他请假");
subject.leave();
System.out.println("代理告诉真正对象请假成功");
}
}

动态代理

动态代理的思维模式与之前的一般模式是一样的,也是面向接口进行编码,创建代理类将具体类隐藏解耦,不同之处在于代理类的创建时机不同,动态代理需要在运行时因需实时创建.

模式结构

和上面类似,需要一个接口和实现了这个接口的真实对象类,然后还要自己定义一个类(调用处理器类,即实现 InvocationHandler 接口),这个类的目的是指定运行时将生成的代理类需要完成的具体任务(包括Preprocess和Postprocess),即代理类调用任何方法都会经过这个调用处理器类。

结构图

代码实现

用动态代理实现上面的场景,需要自定义一个调用处理器类。

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
public class ProxyPattern {
public static void main(String[] args){
/**
* 动态代理测试
*/
ProxyHandler proxyHandler = new ProxyHandler(realSubject);
//动态生成代理对象
Subject proxySubject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(),
proxyHandler);
proxySubject.leave();
}
}
/**
* 动态代理实现上面的例子
*
*/
class ProxyHandler implements InvocationHandler {
private Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args){
Object result = null;
System.out.println("真正对象告诉代理帮他请假");
try{
result = method.invoke(subject, args);
} catch (Exception e){
e.printStackTrace();
}
System.out.println("代理告诉真正对象请假成功");
return result;
}
}

jdk动态代理为什么要求委托对象实现接口

1
2
//创建代理对象
static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

第二个参数是接口,表明这个代理类需要实现哪些接口。
因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),只能针对接口创建代理类,不能针对类创建代理类。

生成的代理类:

1
public final class $Proxy0 extends Proxy implements Subject{}

扩展

代理模式用到的地方很多,比如 spring aop 使用的便是动态代理模式,spring aop 有两种实现方式,一种是jdk动态代理(要求委托对象实现接口);另一种是字节码增强,委托对象可以不实现接口,具体实现如 cglib。

参考资料

代理模式及Java实现动态代理

《大话设计模式》