Java二十三种设计模式 之代理(proxy)
今天我们学习一下静态代理和动态代理
我们来看代码(写一个坦克运行了多少时间):
第一种方法:
public calss Tank implements Movable{
public void move(){
//记录开始的时间
long start = System.currentTimeMillis();
System.out.println("Tank moving claclacla....");
try{
//随机睡眠10秒钟,模拟它开了几秒钟
Thread.sleep(new Random().nextInt(10000));
}catch(InterruptedException e){
e.printStackTrace();
}
//记录结束的时间
long end = System.currentTimeMillis();
//输出结束减开始的时间
System.out.println(end - start);
}
//写方法测试一下
public static void main(String[] args){
Tank t = new Tank();
t.move();
}
}
interface Movable{
void move();
}
第二种方法:
//用代理
public calss Tank implements Movable{
public void move(){
System.out.println("Tank moving claclacla....");
try{
//随机睡眠10秒钟,模拟它开了几秒钟
Thread.sleep(new Random().nextInt(10000));
}catch(InterruptedException e){
e.printStackTrace();
}
}
//写方法测试一下
public static void main(String[] args){new TankTimeProxy(new tank().move();}
}
//时间代理
class TankTimeProxy implements Movable{
Tank tank;
public void move(){
//输出开始的时间
long start = System.currentTimeMillis();
tank.move();
//输出结束的时间
long end = System.currentTimeMillis();
//输出结束减开始的时间
System.out.println(end - start);
}
}
//日志代理
class TanklogProxy implements Movable{
Tank tank;
public void move(){
//开始
System.out.println("start moving...");
tank.move();
//结束
System.out.println("stopped");
}
}
interface Movable{
void move();
}
最终的静态代理:
-
他们共同实现了Movabel这个接口,代理的类型也是Movable,所以他们之间可以相互嵌套。
public calss Tank implements Movable{ public void move(){ System.out.println("Tank moving claclacla...."); try{ //随机睡眠10秒钟,模拟它开了几秒钟 Thread.sleep(new Random().nextInt(10000)); }catch(InterruptedException e){ e.printStackTrace(); } } //写方法测试一下 public static void main(String[] args){new TankTimeProxy(new TankLogProxy(new tank())).move();} } //时间代理 class TankTimeProxy implements Movable{ Movable m; public TankLogProxy(Movable m ){this.m = m;} public void move(){ //输出开始的时间 long start = System.currentTimeMillis(); m.move(); //输出结束的时间 long end = System.currentTimeMillis(); //输出结束减开始的时间 System.out.println(end - start); } } //日志代理 class TanklogProxy implements Movable{ Movable m; public TankLogProxy(Movable m ){this.m = m;} public void move(){ //开始 System.out.println("start moving..."); m.move(); //结束 System.out.println("stopped"); } } interface Movable{ void move();
}
-
我们看一下类图
代理对象和被代理对象都实现了Movable接口,将需要的操作添加到代理对象中的move()方法,接下里调用被代理对象的move()方法,这样就可以嵌套了,因为TimeProxy和LogProxy都实现了Movable接口,所以都可以添加到m:Movable这个被代理对象里
静态代理我们说完了,下面来说说动态代理
-
动态代理之所以称为动态,是因为代理类在运行时由Proxy类产生,这就大大减少了需要我们手工设计代理类的数量,在实际应用中,Proxy通过类装载器和需要实现的接口来产生代理类,即Proxy.getProxyClass(ClassLoader, Class[]),返回为代理类的Class实例。
public calss Tank implements Movable{ //模拟坦克移动了一段时间 public void move(){ System.out.println("Tank moving claclacla...."); try{ //随机睡眠10秒钟,模拟它开了几秒钟 Thread.sleep(new Random().nextInt(10000)); }catch(InterruptedException e){ e.printStackTrace(); } } public static void main(String[] args){ Tank tank = new Tank(); Movable m = (Movable)Proxy.newProxyInstonce(Tank.calss.getCalssLoader(),new Class[]{Movable.class},//tank.calss.getInterfaces() newLogHander(tank); ); m.move(); } } class LogHander implements InvocationHandler{ Tank tank; public LogHander(Tank tank){ this.tank = tank; } public Object invoke(Object proxy,Method methos,Object[] args){ System.out.println("method" + method.getName()+"start..."); Object o method.incoke(tank,args); System.out.println("method" + method.getName()+"end..."); return o; } } interface Movable{ void move(); }
-
代理就是方法在调用的时候我们做了一些自己的处理,动态代理生成的时候,move方法被调用,其实调用的invok方法,怎么调用的呢,我们看下一个版本
public calss Tank implements Movable{ //模拟坦克移动了一段时间 public void move(){ System.out.println("Tank moving claclacla...."); try{ //随机睡眠10秒钟,模拟它开了几秒钟 Thread.sleep(new Random().nextInt(10000)); }catch(InterruptedException e){ e.printStackTrace(); } } public static void main(String[] args){ Tank tank = new Tank(); //jdk.proxy.Proxy6enerator代理产生器,sace6eneratedFiles代理产生器中的参数,把产生的file给save下来。 System.getProperties().put("jdk.proxy.Proxy6enerator.sace6eneratedFiles","true"); Movable m = (Movable)Proxy.newProxyInstonce(Tank.calss.getCalssLoader(),new Class[]{Movable.class},//tank.calss.getInterfaces() newLogHander(tank); ); m.move(); } } class LogHander implements InvocationHandler{ Tank tank; public LogHander(Tank tank){ this.tank = tank; } public Object invoke(Object proxy,Method methos,Object[] args){ System.out.println("method" + method.getName()+"start..."); Object o method.incoke(tank,args); System.out.println("method" + method.getName()+"end..."); return o; } } interface Movable{ void move(); }
-
save下来之后会出现一个为com文件夹,他默认的把Proxy生成的$Proxy0.class类生成到这里-
-
总结:
1.代理解决的问题当两个类需要通信时,引入第三方代理类,将两个类的关系解耦,让我们只了解代理类即可,而且代理的出现还可以让我们完成与另一个类之间的关系的统一管理,但是切记,代理类和委托类要实现相同的接口,我们这里实现的Movable接口,因为代理真正调用的还是委托类的方法。
2.代理可以对实现类进行统一的管理,如在调用具体实现类之前,需要打印日志等信息,这样我们只需要添加一个代理类,在代理类中添加打印日志的功能,然后调用实现类,这样就避免了修改具体实现类。满足我们所说的开闭原则,例如我们举的例子中,日志代理和时间代理是可以互换的,谁先输出谁后输出都可以。