前言:

  代理模式作为常见的设计模式之一,在项目开发中不可或缺。本文就尝试着揭开代理的神秘面纱,也欢迎各路人批评指正!

1.如何实现代理:

【假设有个关于汽车移动(move)的计时需求】
设计:Moveable接口,一个Car的实现类;两个代理CarTimer,TimeHandler.UML图如下:


1)继承

 package com.gdufe.proxy;

 import java.util.Random;

 public class CarTimer extends Car {

     @Override
public void move() {
long start=System.currentTimeMillis();
super.move(); //调用父类的move()方法
try{
Thread.sleep(new Random().nextInt(10000));
}catch(Exception e){
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println("I'm time machine.Time for moving:"+(end-start));
}
}


2)组合

 package com.gdufe.proxy;

 import java.util.Random;

 public class TimeHandler implements Moveable {
private Moveable m;
public TimeHandler(Moveable m) {
this.m = m;
}
@Override
public void move() {
long start=System.currentTimeMillis();
m.move();
try{
Thread.sleep(new Random().nextInt(10000));
}catch(Exception e){
e.printStackTrace();
}
long end=System.currentTimeMillis();
System.out.println("I'm time machine.Time for moving:"+(end-start));
} }

客户端代码:

 package com.gdufe.proxy;

 public class Client {
public static void main(String[] args) {
System.out.println("继承实现代理:");
new CarTimer().move();
System.out.println("组合实现代理:");
new TimeHandler(new Car()).move();
}
}


输出结果:

继承实现代理:
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

 package com.gdufe.proxy;

 public class TurnLeft implements Moveable {
private Moveable m;
public TurnLeft(Moveable m) {
this.m = m;
}
@Override
public void move() {
System.out.println("turn left...");
m.move();
} }

TurnRight.java

 package com.gdufe.proxy;

 public class TurnRight implements Moveable {

     private Moveable m;
public TurnRight(Moveable m) {
this.m = m;
}
@Override
public void move() {
System.out.println("turn right");
m.move();
} }

客户端代码:

 package com.gdufe.proxy;

 public class Client0 {

     /**
* @param args
*/
public static void main(String[] args) {
System.out.println("Turn right,then left before moving:");
test1(); System.out.println("Turn left,then right before moving:");
test2();
}
//对接口的实现内外包装
private static void test1() {
Car car = new Car();
Moveable m1 = new TurnLeft(car);
Moveable m2 = new TurnRight(m1);
m2.move();
}
public static void test2(){
Car car = new Car();
Moveable m1 = new TurnRight(car);
Moveable m2 = new TurnLeft(m1);
m2.move();
} }

输出结果:

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

 package com.gdufe.proxy;

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; public class LogHandler implements InvocationHandler { //被代理对象
private Object proxied; public LogHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("I'm log machine.");
return method.invoke(proxied);
} }

客户端代码:

 package com.gdufe.proxy;

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; public class Client1 { /**
* @param args
*/
public static void main(String[] args) { //单独创建Moveable接口对象
Moveable m=null;
m = new Car();
m = new TimeHandler(m); //初始化LogHandler实现的InvacationHandler接口
InvocationHandler h = new LogHandler(m);
m = (Moveable) Proxy.newProxyInstance(
Moveable.class.getClassLoader(),
new Class[] {Moveable.class},h); //m在动态代理处理完后,move()方法已被修改
m.move();
} }

输出结果:

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()方法上】
----------
客户端代码:

 package com.gdufe.proxy;

 import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy; public class Client2 { /**
* @param args
*/
public static void main(String[] args) {
Moveable m=null;
m = new Car(); Speakable s =null;
s = new Driver(); InvocationHandler h = null;
h=new LogHandler(m);
m = (Moveable) Proxy.newProxyInstance(Moveable.class.getClassLoader(),new Class[] {Moveable.class},h);
m.move(); //司机被代理
h = new LogHandler(s);
s = (Speakable)Proxy.newProxyInstance(Speakable.class.getClassLoader(),new Class[]{Speakable.class}, h);
s.speak();
} }

输出结果:

I'm log machine.
Car moving...
I'm log machine.
Driver speak...

  在汽车move()跟司机speak()之前,都自动实现LogHandler操作!

-----------------------------------------------------

后语:

通过上述例子,总结动态代理优点:
·适用任何类的任何方法;
·使用灵活,可随时将代理工具类加入或抽出。

  动态代理是Java语言的精华所在,很多的开发框架都是基于其内部原理。本人目前对动态代理的理解也仅限于此,欢迎对Java有深度学识的朋友拍砖,谢谢~

神秘代理-Proxy的更多相关文章

  1. 代理(Proxy)和反射(Reflection)

    前面的话 ES5和ES6致力于为开发者提供JS已有却不可调用的功能.例如在ES5出现以前,JS环境中的对象包含许多不可枚举和不可写的属性,但开发者不能定义自己的不可枚举或不可写属性,于是ES5引入了O ...

  2. 深度揭秘ES6代理Proxy

    最近在博客上看到关于ES6代理的文章都是一些关于如何使用Proxy的例子,很少有说明Proxy原理的文章,要知道只有真正掌握了一项技术的原理,才能够写出精妙绝伦的代码,所以我觉得有必要写一篇关于深刻揭 ...

  3. Webpack代理proxy配置,解决本地跨域调试问题,同时允许绑定host域名调试

    Webpack代理proxy配置,解决本地跨域调试问题,同时允许绑定host域名调试 会撸码的小马 关注 2018.05.29 17:30* 字数 212 阅读 1488评论 0喜欢 2 接到上一章, ...

  4. 浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance

    浅谈Java代理一:JDK动态代理-Proxy.newProxyInstance java.lang.reflect.Proxy:该类用于动态生成代理类,只需传入目标接口.目标接口的类加载器以及Inv ...

  5. 初识代理——Proxy

    无处不在的模式——Proxy 最近在看<设计模式之禅>,看到代理模式这一章的时候,发现自己在写spring项目的时候其实很多时候都用到了代理,无论是依赖注入.AOP还是其他,可以说是无处不 ...

  6. JAVA设计模式-动态代理(Proxy)示例及说明

    在Mybatis源码解析,一步一步从浅入深(五):mapper节点的解析文章的最后部分,我们提到了动态代理的概念,下面我们就简单了解一下动态代理. 一,概念 代理设计模式的目的就是在不直接操作对象的前 ...

  7. JAVA设计模式-动态代理(Proxy)源码分析

    在文章:JAVA设计模式-动态代理(Proxy)示例及说明中,为动态代理设计模式举了一个小小的例子,那么这篇文章就来分析一下源码的实现. 一,Proxy.newProxyInstance方法 @Cal ...

  8. java动态代理--proxy&cglib

    大纲 代理 proxy cglib 小结 一.代理 为什么要用代理?其实就是希望不修改对象的情况下,增强对象. 静态代理: 静态代理模式,需要代理类和目标类实现同一接口,代理类的方法调用目标类的方法, ...

  9. 动态代理proxy与CGLib的区别

    什么是代理? 静态代理与动态代理 静态代理实例 JDK动态代理实例 CGLib 简介 CGLib 与JDK动态代理的区别 代理模式是Java中常见的一种模式,英文名字叫走Proxy或者Surrogat ...

随机推荐

  1. java访问修饰符

    了解面向对象思想的同学们,都知道"封装"这一基本特征,如何正确运用访问修饰符,恰恰能体现出封装的好坏. java访问修饰符有四个: 1)public:访问权限最高,其修饰的类.类变 ...

  2. Java全角、半角字符的关系以及转换

    如果搞明白了Java中全角字符和半角字符之间的关系,那他们之间的转换就不是个麻烦事儿.你只需要对这个关系有那么一个印象就足够了. 全角字符与半角字符的关系 通过下面的代码能看到Java中所有字符以及对 ...

  3. ORACLE实现自定义序列号生成

    实际工作中,难免会遇到序列号生成问题,下面就是一个简单的序列号生成函数 (1)创建自定义序列号配置表如下: --自定义序列 create table S_AUTOCODE ( pk1 ) primar ...

  4. 异常:java.lang.LinkageError: loader constraint violation: when resolving interface method

    异常:java.lang.LinkageError: loader constraint violation: when resolving interface method "javax. ...

  5. SAP CRM 复用视图

    在设计任何视图或组件的时候,我们需要以可复用的方式来设计它.UI组件设计的主要目标即可复用. 例如:几乎每个事务都要处理合作伙伴(客户).如果我们想要在Web UI显示那些合作伙伴,需要设计一个视图. ...

  6. iOS之数字的格式化

    //通过NSNumberFormatter,同样可以设置NSNumber输出的格式.例如如下代码: NSNumberFormatter *formatter = [[NSNumberFormatter ...

  7. Android 防止多次点击事件

    恐怕大家都会遇到这样的问题,一个点击事件多次触发,导致,同样的内容提交了多次,或者说弹出多个页面... 下面是简单的方案,大家可以试一试 原理很简单,当我们第一次点击的时候,把按钮变成不可点击状态. ...

  8. 安开发卓之Notification(一)代码直接能用

    Notification是Android中很理想的一种显示提示信息的方法,它可以将应用程序的信息传递到我们的Android桌面状态栏,采用这种消息传递方式不会影响到用户对手机的正常使用.而且Notif ...

  9. IOS 杂笔-9 (MD5 加密)

    首先是一段对MD5的简介 *出自一位大牛之手* Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护 ...

  10. Android笔记——Android自定义控件

    目录: 1.自定义控件概述 01_什么是自定义控件 Android系统中,继承Android系统自带的View或者ViewGroup控件或者系统自带的控件,并在这基础上增加或者重新组合成我们想要的效果 ...