代理模式

代理模式提供了一种方法来控制对目标类的访问,当为一个目标对象创建代理对象以后,代理对象将拦截对目标对象的方法调用,从而控制对于目标类的访问。代理也可以看成是一个包装类,对目标对象的包装。

代理模式的意图

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;
    }

    @Override
    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;
}

@Override
public void enter(Wizard wizard) {
if (numWizards < NUM_WIZARDS_ALLOWED) {
tower.enter(wizard);
numWizards++;
} else {
LOGGER.info("{} is not allowed to enter!", wizard);
}
}
}

WizardTowerProxy2WizardTower 的动态代理调用处理器,对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
public 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;
}

@Override
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
23
public 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
10
11: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() 方法来创建一个代理类,并拦截目标类的方法调用。其中,动态代理在很多的框架中都有使用,SpringAOP就是基于动态代理来实现的,构建切面拦截对于方法的调用。Mybatis 中的面向接口编程也是为每个Mpper创建一个MapperProxy,来实现具体的调用。