Java 设计模式之代理模式

代理模式是开发中非常常见的一种设计模式,属于结构型模式。代理模式的动机简单点说就是,有时不方便或者不能直接访问一个对象时,可以通过一个代理对象来实现间接访问。在现实中有很多类似的例子:不想走那么远去火车站买票,就去近一点的代售点买票。不方便约明星谈合作,就和明星的经纪人谈。没有带现金,于是用手机微信作为代理买单。下面就具体分析代理模式以及动态代理的原理。

代理模式(Proxy Pattern)

定义

给某个对象提供一个代理,并由代理对象控制原对象的引用。属于对象结构型模式。

结构

代理模式主要包括三个角色:

  1. Subject,对象的接口

  2. RealSubject,实际对象

  3. ProxySubject,代理对象,持有实际对象的引用

代理模式比较简单,就不再描述示例代码了。

有两点需要注意的:代理对象与实际对象实现相同的接口;代理对象持有实际对象的引用,调用代理对象方法最终会调用实际对象的实现。

与其他模式的区别

  • 代理模式提供的接口不变,适配器模式是为实际对象提供一个不同的接口,装饰器模式是提供一个额外的接口。

  • 代理模式和装饰器模式的结构非常相似,都持有原有对象的引用。

动态代理

上面代理模式的实现是静态代理,还有运行时生成代理类的动态代理,比较常见的是 Java 中动态代理机制,Retrofit 框架就是通过 Java 的动态代理把方法调用转换为网络请求。

Java 动态代理机制

先看下如何使用:

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
// 需要代理的对象接口
interface Subject {
void sayHello();
void sayGoodBye();
}
// 实际对象
public class RealSubject implements Subject {
@Override
public void sayHello() {
System.out.println("hello!");
}
@Override
public void sayGoodBye() {
System.out.println("goodbye!");
}
}
// 代理处理器,并不是代理类
public class InvocationImp implements InvocationHandler {
private Object obj; // 代理的实际对象的引用
public InvocationImp(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invocation");
Object result = method.invoke(obj, args); // 调用实际对象的方法
System.out.println("after invocation");
return result;
}
}
// 调用
Subject realSubject = new RealSubject();
InvocationHandler invocationHandler = new InvocationImp(realSubject);
Subject proxySubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), invocationHandler);
proxySubject.sayHello();
proxySubject.sayGoodBye();

上面代码的输出:

1
2
3
4
5
6
before invocation
hello!
after invocation
before invocation
goodbye!
after invocation

Java 动态代理机制的原理

代理对象是通过Proxy.newProxyInstance方法生成的,代理对象的类通过 jd-jui 工具反编译后大致如下:

1
2
3
public final class ProxySubject extends Proxy implements Subject{
...
}

动态生成的代理类继承自java.lang.reflect.Proxy类,实现了需要代理的接口 Subject,而静态代理中代理类是我们自己写出来的。

动态代理的调用链:调用 proxyObject 方法 -> InvocationHandler.invoke -> 调用 realObject 方法

静态代理的调用链:调用 proxyObject 方法 -> 调用 realObject 方法

Java 动态代理机制的优缺点

优点:

  • 解耦代理类与实际类,InvocationHandler 中传入的实际对象是不确定的,代理类不知道代理的具体是哪个对象。

  • 减少代码量,代理多个接口时,InvocationHandler 实现可以不变

  • 可以实现切片编程(AOSP)

缺点:

  • 生成代理对象时使用了反射,性能有所下降。

  • Java 动态代理只能根据接口创建,不能根据类或抽象类创建。

参考文章

END
Johnny Shieh wechat
我的公众号,不只有技术,还有咖啡和彩蛋!