代理模式提供了一种方法来控制对目标类的访问,当为一个目标对象创建代理对象以后,代理对象将拦截对目标对象的方法调用,从而控制对于目标类的访问。代理也可以看成是一个包装类,对目标对象的包装。
代理模式的意图
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,来实现具体的调用。