代理模式提供了一种方法来控制对目标类的访问,当为一个目标对象创建代理对象以后,代理对象将拦截对目标对象的方法调用,从而控制对于目标类的访问。代理也可以看成是一个包装类,对目标对象的包装。
代理模式的意图
Provide a surrogate or placeholder for another object to control access to it.
为其他的对象提供一个代理,从而控制 client
对于目标对象的访问。
代理模式的适用场景
下面是几种常见的代理模式适用的情况:
- 当需要控制对于目标对象的访问时,比如权限控制。
- 懒加载(
Lazy Initialization
)可以使用动态代理实现。代理类和目标类实现了相同的接口,第一次调用的时候代理对象将加载目标对象,并将以后的调用都委托给它。 - 日志记录。通过拦截方法调用实现。
- 代理模式还可以用来促进网络连接以及计算对于一个对象的引用。
具体实例
下面这个例子使用的是静态代理,实现的方法是通过与目标对象实现同样的接口,并持有一个目标对象的引用来实现;本例还使用了动态代理的方法来实现动态代理。
主要涉及如下几个类:
Wizard
类。WizardTower
接口。IvoryTower
类,实现了WizardTower
接口。WizardTowerProxy
类。WizardTower
的静态代理类,持有一个WizardTower
的引用。WizardTowerProxy2
类。WizardTower
的动态代理调用处理器。
类之间的关系如下图:Wizard
类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
*
* Wizard
*
*/
public class Wizard {
private final String name;
public Wizard(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
WizardTower
接口:1
2
3
4
5
6
7/**
* WizardTower interface
*/
public interface WizardTower {
void enter(Wizard wizard);
}
IvoryTower
类,实现了 WizardTower
接口:1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
*
* 被代理的对象
*
*/
public class IvoryTower implements WizardTower {
private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);
public void enter(Wizard wizard) {
LOGGER.info("{} enters the tower.", wizard);
}
}
WizardTowerProxy
类。WizardTower
的静态代理类,持有一个WizardTower
的引用: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/**
*
* The proxy controlling access to the IvoryTower
*
*/
public class WizardTowerProxy implements WizardTower {
private static final Logger LOGGER = LoggerFactory.getLogger(WizardTowerProxy.class);
private static final int NUM_WIZARDS_ALLOWED = 3;
private int numWizards;
private final WizardTower tower;
public WizardTowerProxy(WizardTower tower) {
this.tower = tower;
}
public void enter(Wizard wizard) {
if (numWizards < NUM_WIZARDS_ALLOWED) {
tower.enter(wizard);
numWizards++;
} else {
LOGGER.info("{} is not allowed to enter!", wizard);
}
}
}
WizardTowerProxy2
是 WizardTower
的动态代理调用处理器,对WizardTower
的方法调用将会被拦截: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
29public class WizardTowerProxy2 implements InvocationHandler
{
WizardTower wizardTower;
private static final Logger LOGGER = LoggerFactory.getLogger(WizardTowerProxy.class);
private static final int NUM_WIZARDS_ALLOWED = 3;
private int numWizards;
public WizardTowerProxy2(WizardTower wizardTower)
{
this.wizardTower = wizardTower;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
if (numWizards < NUM_WIZARDS_ALLOWED) {
method.invoke(wizardTower, args);
numWizards++;
} else {
LOGGER.info("{} is not allowed to enter!", args);
}
return null;
}
}
测试代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class App {
/**
* Program entry point
*/
public static void main(String[] args) {
IvoryTower tower = new IvoryTower();
WizardTowerProxy proxy = new WizardTowerProxy(tower);
proxy.enter(new Wizard("Red wizard"));
proxy.enter(new Wizard("White wizard"));
proxy.enter(new Wizard("Black wizard"));
proxy.enter(new Wizard("Green wizard"));
proxy.enter(new Wizard("Brown wizard"));
WizardTower proxytower = (WizardTower) Proxy.newProxyInstance(tower.getClass().getClassLoader(), tower.getClass().getInterfaces(),new WizardTowerProxy2(tower));
proxytower.enter(new Wizard("Red wizard"));
proxytower.enter(new Wizard("White wizard"));
proxytower.enter(new Wizard("Black wizard"));
proxytower.enter(new Wizard("Green wizard"));
proxytower.enter(new Wizard("Brown wizard"));
}
}
运行结果:1
2
3
4
5
6
7
8
9
1011:04:29.158 [main] INFO com.iluwatar.proxy.IvoryTower - Red wizard enters the tower.
11:04:29.162 [main] INFO com.iluwatar.proxy.IvoryTower - White wizard enters the tower.
11:04:29.162 [main] INFO com.iluwatar.proxy.IvoryTower - Black wizard enters the tower.
11:04:29.162 [main] INFO com.iluwatar.proxy.WizardTowerProxy - Green wizard is not allowed to enter!
11:04:29.162 [main] INFO com.iluwatar.proxy.WizardTowerProxy - Brown wizard is not allowed to enter!
11:04:29.166 [main] INFO com.iluwatar.proxy.IvoryTower - Red wizard enters the tower.
11:04:29.167 [main] INFO com.iluwatar.proxy.IvoryTower - White wizard enters the tower.
11:04:29.167 [main] INFO com.iluwatar.proxy.IvoryTower - Black wizard enters the tower.
11:04:29.167 [main] INFO com.iluwatar.proxy.WizardTowerProxy - Green wizard is not allowed to enter!
11:04:29.167 [main] INFO com.iluwatar.proxy.WizardTowerProxy - Brown wizard is not allowed to enter!
总结
本文介绍了java设计模式中的代理模式,实现方式包括静态代理和动态代理。静态代理指的是代理类和目标类实现同样的接口,并且代理类持有一个目标类的引用;动态代理指的是通过Prxoy.newInstance()
方法来创建一个代理类,并拦截目标类的方法调用。其中,动态代理在很多的框架中都有使用,Spring
的AOP
就是基于动态代理来实现的,构建切面拦截对于方法的调用。Mybatis
中的面向接口编程也是为每个Mpper
创建一个MapperProxy
,来实现具体的调用。