前言:
代理模式作为常见的设计模式之一,在项目开发中不可或缺。本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正!
1.如何实现代理:
【假设有个关于汽车移动(move)的计时需求】 设计:Moveable接口,一个Car的实现类;两个代理CarTimer,TimeHandler.UML图如下:
1)继承
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 import java.util.Random;
4
5 public class CarTimer extends Car {
6
7 @Override
8 public void move() {
9 long start=System.currentTimeMillis();
10 super.move(); //调用父类的move()方法
11 try{
12 Thread.sleep(new Random().nextInt(10000));
13 }catch(Exception e){
14 e.printStackTrace();
15 }
16 long end=System.currentTimeMillis();
17 System.out.println("I'm time machine.Time for moving:" (end-start));
18 }
19 }
2)组合
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 import java.util.Random;
4
5 public class TimeHandler implements Moveable {
6 private Moveable m;
7 public TimeHandler(Moveable m) {
8 this.m = m;
9 }
10 @Override
11 public void move() {
12 long start=System.currentTimeMillis();
13 m.move();
14 try{
15 Thread.sleep(new Random().nextInt(10000));
16 }catch(Exception e){
17 e.printStackTrace();
18 }
19 long end=System.currentTimeMillis();
20 System.out.println("I'm time machine.Time for moving:" (end-start));
21 }
22
23 }
客户端代码:
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 public class Client {
4 public static void main(String[] args) {
5 System.out.println("继承实现代理:");
6 new CarTimer().move();
7 System.out.println("组合实现代理:");
8 new TimeHandler(new Car()).move();
9 }
10 }
输出结果:
代码语言:javascript复制继承实现代理:
Car moving...
I'm time machine.Time for moving:7080
组合实现代理:
Car moving...
I'm time machine.Time for moving:5169
分析:从上述例子实现当中,我们第一感觉自然是分不出两种代理的实现方式孰优孰劣。且继续往下面看。
2.灵活代理-接口切换
【假设现在特殊需求不确定:“汽车移动之前先往左还是先往右”】 很明显,我们此时若使用继承的方式实现代理,则后续很不容易维护,而且会形成臃肿的继承链;但使用接口的方式我们发现仅需要两个代理类:TurnLeft,TurnRight。而且,不管后续需求如何都只需要做简单的调整。UML图如下:
----------
TurnLeft.java
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 public class TurnLeft implements Moveable {
4 private Moveable m;
5 public TurnLeft(Moveable m) {
6 this.m = m;
7 }
8 @Override
9 public void move() {
10 System.out.println("turn left...");
11 m.move();
12 }
13
14 }
TurnRight.java
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 public class TurnRight implements Moveable {
4
5 private Moveable m;
6 public TurnRight(Moveable m) {
7 this.m = m;
8 }
9 @Override
10 public void move() {
11 System.out.println("turn right");
12 m.move();
13 }
14
15 }
客户端代码:
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 public class Client0 {
4
5 /**
6 * @param args
7 */
8 public static void main(String[] args) {
9 System.out.println("Turn right,then left before moving:");
10 test1();
11
12 System.out.println("Turn left,then right before moving:");
13 test2();
14 }
15 //对接口的实现内外包装
16 private static void test1() {
17 Car car = new Car();
18 Moveable m1 = new TurnLeft(car);
19 Moveable m2 = new TurnRight(m1);
20 m2.move();
21 }
22 public static void test2(){
23 Car car = new Car();
24 Moveable m1 = new TurnRight(car);
25 Moveable m2 = new TurnLeft(m1);
26 m2.move();
27 }
28
29 }
输出结果:
代码语言:javascript复制Turn right,then left before moving:
turn right
turn left...
Car moving...
Turn left,then right before moving:
turn left...
turn right
Car moving...
========================
3.动态代理:
其实,不管是继承还是组合的方式,我们上面实现的都仅仅是“静态代理”,也是我们平时用的比较多的。现在,我们开始聊聊Java的神器---“动态代理”。 【假设现在需要实现一个“万能”的日志工具,即不管对任何类的任何方法都可以动态对其进行日志操作】 例如:上面的例子中,请思考如何实现在汽车移动之前进行日志操作? 常规的静态代理方式当然可以实现,下面我们就利用Java中的Proxy类进行实现。UML图如下:
添加关键类:LogHandler.java
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Method;
5
6 public class LogHandler implements InvocationHandler {
7
8 //被代理对象
9 private Object proxied;
10
11 public LogHandler(Object proxied) {
12 this.proxied = proxied;
13 }
14 @Override
15 public Object invoke(Object proxy, Method method, Object[] args)
16 throws Throwable {
17 System.out.println("I'm log machine.");
18 return method.invoke(proxied);
19 }
20
21 }
客户端代码:
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Proxy;
5
6 public class Client1 {
7
8 /**
9 * @param args
10 */
11 public static void main(String[] args) {
12
13 //单独创建Moveable接口对象
14 Moveable m=null;
15 m = new Car();
16 m = new TimeHandler(m);
17
18 //初始化LogHandler实现的InvacationHandler接口
19 InvocationHandler h = new LogHandler(m);
20 m = (Moveable) Proxy.newProxyInstance(
21 Moveable.class.getClassLoader(),
22 new Class[] {Moveable.class},h);
23
24 //m在动态代理处理完后,move()方法已被修改
25 m.move();
26 }
27
28 }
输出结果:
代码语言:javascript复制I'm log machine.
Car moving...
I'm time machine.Time for moving:110
分析:
上述的实现代码对于刚接触Java的朋友来说估计比较费解。要理解其过程,至少对java的反射机制有一定的理解。看穿了的话,其实动态代理的关键环节,就在newProxyInstance()操作上。代码实现的关键步骤:
Step1:初始化被代理的对象(如上图中的TimeHandler);
Step2:创建一个实现了InvocationHandler接口的类,在invoke()方法进行你希望执行的代理操作(如上图的LogHandler);
Step3:将通过Proxy拿到的新对象赋给最终要实现的接口,最后调用该接口方法(如代码中的“m.move()”)。
---------------------------------------------------------------------
有朋友可能犯迷糊了,动态代理的内部实现过程呢?Proxy是怎样一步一步识别到Car的呢?
请看图:
(注意:$Proxy类是虚拟存在的,在Java API中是找不到的。也就是说,它只存在于中间过程。所以,为方便大家理解就加上了。)
那么,到底动态代理适用于什么情形呢?从上面汽车的简单日志实例也许还难以看出。下面我们再引入一个测试。 【假设现在增加一个司机(Driver)类,其实现了Speakable接口;很明显他跟汽车没有很直接的关联,那么现在我们利用动态代理的方式将上面的LogHandler加到司机的speak()方法上】 ---------- 客户端代码:
代码语言:javascript复制 1 package com.gdufe.proxy;
2
3 import java.lang.reflect.InvocationHandler;
4 import java.lang.reflect.Proxy;
5
6 public class Client2 {
7
8 /**
9 * @param args
10 */
11 public static void main(String[] args) {
12 Moveable m=null;
13 m = new Car();
14
15 Speakable s =null;
16 s = new Driver();
17
18 InvocationHandler h = null;
19 h=new LogHandler(m);
20 m = (Moveable) Proxy.newProxyInstance(Moveable.class.getClassLoader(),new Class[] {Moveable.class},h);
21 m.move();
22
23 //司机被代理
24 h = new LogHandler(s);
25 s = (Speakable)Proxy.newProxyInstance(Speakable.class.getClassLoader(),new Class[]{Speakable.class}, h);
26 s.speak();
27 }
28
29 }
输出结果:
代码语言:javascript复制I'm log machine.
Car moving...
I'm log machine.
Driver speak...
在汽车move()跟司机speak()之前,都自动实现LogHandler操作!
-----------------------------------------------------
后语:
通过上述例子,总结动态代理优点: ·适用任何类的任何方法; ·使用灵活,可随时将代理工具类加入或抽出。
动态代理是Java语言的精华所在,很多的开发框架都是基于其内部原理。本人目前对动态代理的理解也仅限于此,欢迎对Java有深度学识的朋友拍砖,谢谢~