代理的意思很好理解,它借鉴了我们日常所用的代理的意思:就是本来该自己亲自去做的某件事,由于某种原因不能直接做,而只能请人代替你做,这个被你请来做事的人就是代理。比如过春节要回家,由于你要上班,没时间去买票,就得票务中介代你购买,这就是一种代理模式。这个情景可以形象的描述如下:

class:火车站
{
卖票:
{……}
}
火车站是卖票的地方,我们假设只能在火车站买到票。卖票的动作实质是火车站类完成的。
Class:票务中介
{
卖票:
{
收中介费;
火车站.卖票;
}
}
顾客找票务中介买票的时候,调用票务中介.卖票。票务中介其实做了两件事,一是去火车站买票,二是不能白帮你卖票,肯定要收中介费。而你得到的好处是不用直接去火车站买票,节省了买票的时间用来上班。
以上我们简单模拟了代理模式的情景和为什么要使用代理模式,下面我们以一个例子来具体分析一下JAVA中的代理模式。
假设有一个信息管理系统,用些用户有浏览信息的权限,有些用户有浏览、添加和修改信息的权限,还有些用户有除了上述的权限,还有删除信息的权限,那么我们最容易想到的做法如下:
public class ViewAction
{
//由userId计算权限
……
String permission = ……;
if(permission.equals(Constants.VIEW))
{
System.out.println(“You could view the information……”);
……
}
}
其他的动作都和浏览信息的动作差不多。我们来看这样的类,很容易看出它的一些缺点来:第一、它把权限计算和动作执行都放在一个类里,两者的功能相互混在一起,容易造成思路的混乱,而且修改维护和测试都不好;一句话来说,它不满足单一职责原则。第二是客户调用的时候依赖具体的类,造成扩展和运行期内的调用的困难,不满足依赖颠倒原则。
既然有这么多的问题,我们有必要对该类进行重新设计。其实大家早已想到,这个类应该使用代理模式。是啊,和我们买火车票的动作一样,动作类不能直接执行那个动作,而是要先检查权限,然后才能执行;先检查权限,后执行的那各类其实就是一个代理类,修改后的代码如下:
public interface Action
{
public void doAction();
}
首先是设计一个接口,用来满足依赖颠倒原则。
Public class ViewAction implements Action
{
public void doAction()
{
//做View的动作
System.out.println(“You could view the information……”);
……
}
}
这个类跟火车站一样,是动作的真实执行者。
Public class ProxyViewAction implements Action
{
private Action action = new ViewAction();
public void doAction()
{
//调用权限类的方法取得用户权限
if(Permission.getPermission(userId).equals(Constants.VIEW))
{
action.doAction();
}
}
}
这是代理类,很容易理解。在我们的ProxyViewAction类中,除了做了客户真正想要做的动作:doAction()以外,还进行了额外的动作检查用户的权限。而作核心动作doAction()是在一个干干净净的类:ViewAction中进行,这个类只做核心动作,对其他的不关心,满足了单一职责原则。
客户端通过调用代理类来执行动作,而代理类一是将权限判断和动作的执行分离开来,满足了单一职责原则;二是实现了一个接口,从而满足了依赖颠倒原则。比第一个思路好了很多。
代理又被称为委派,说的是代理类并不真正的执行那个核心动作,而是委派给另外一个类去执行,如ProxyView类中,ProxyView类并没有真正执行doAction()方法,而是交给ViewAction类去执行。
我们再来看代理类ProxyViewAction,可以看到它不仅依赖于接口Action,而且依赖于具体的实现ViewAction。这样对我们的系统扩展很不利,比如我们有Add动作、Delete动作、Modify动作等等,我们需要对每一个动作都写一个代理类,而这些代理类都做同样的事情,先进行权限判断,然后再委派。所以我们需要对这些代理再进行一次抽象,让它只依赖接口Action,而不依赖于具体的实现。
要实现这样的想法,我们需要将代理类中的具体实现提走,让代理的使用者在运行期提供具体的实现类,即所谓的依赖注入,如下:
Public class ProxyAction implements Action
{
private Action action;
public ProxyAction(Action action)
{
this.action = action;
}
public void doAction()
{
//调用权限类的方法取得用户权限
if(Permission.getPermission(userId).equals(action.getClass().getName()))
{
action.doAction();
}
}
}
这样,我们就将所有实现了Action接口的实现使用一个代理类来代理它们。除了ViewAction类能用,以后扩展的AddAction、       ModifyAction、DeleteAction类等等,都可以使用一个代理类:ProxyAction。
而我们的客户端类似如下:
Action action = ProxyAction(new ViewAction);
Action.doAction();
通过对代理类的依赖注入,我们使得代理类初步有了一定扩展性。但是我们还要看到,这个代理类依赖于某一个确定的接口。这仍然不能满足我们的实际要求,如我们的系统的权限控制一般是整个系统级的,这样系统级的权限控制,我们很难在整个系统里抽象出一个统一的接口,可能会有多个接口,按照上面的代理模式,我们需要对每一个接口写一个代理类,同样,这些类的功能都是一样的。这显然不是一个好地解决办法。
基于上面的原因,我们需要解决一个系统在没有统一的接口的情况下,对一些零散的对象的某一些动作使用代理模式的问题。JAVA API为我们引入了动态代理或动态委派的技术。
动态代理的核心是InvocationHandler接口,要使用动态代理就必须实现该接口。这个接口的委派任务是在invoke(Object proxy, Method m, Object[] args)方法里面实现的:
//在调用核心功能之前作一些动作
……
//调用核心功能
m.invoke(obj, args);
//在调用核心功能以后做一些动作
……
我们可以看到动态代理其实用的是反射机制来调用核心功能的:m.invoke(obj, args);正是这种反射机制的使用使得我们调用核心功能更加灵活,而不用依赖于某一个具体的接口,而是依赖于Object对象。
下面我们来具体看看动态代理或动态委派如何使用:
public class ProxyAction implements InvocationHandler {
private Object action;
public ProxyAction(Object action)
{
this.action = action;
}
public static Object getInstance(Object action)
{
return Proxy.newProxyInstance(action.getClass().getClassLoader(),
action.getClass().getInterfaces(),new ProxyAction(action));
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
Object result;
try {
//在委派之前作动作,如权限判断等
System.out.println("before method " + m.getName());
//进行委派
result = m.invoke(action, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: "
+ e.getMessage());
} finally {
//在委派之后做动作
System.out.println("after method " + m.getName());
}
return result;
}
}
这个代理类,首先是实现了InvocationHandler接口;然后在getInstance()方法里得到了代理类的实例;在invoke()方法里实现代理功能,也很简单。
下面我们来看客户端:
Action action = (Action)ProxyAction.getInstance(new ViewAction());
Action.doAction();
我们可以看到代理类对接口的依赖也转移到了客户端上,这样,代理类不依赖于某个接口。对于同样的代理类ProxyAction,我们也可以有如下的客户端调用:
Engine engine = (Engine)ProxyAction.getInstance(new EngineImpl());
Engine.execute();
只要engineImpl类实现了Engine接口,就可以像上面那样使用。
现在我们可以看到,动态代理的确是拥有相当的灵活性。但我们同时也看到了,这个代理类写起来比较麻烦,而且也差不多每次都写这样千篇一律的东西,只有委派前的动作和委派后的动作在不同的代理里有着不同,其他的东西都需要照写。如果这样的代理类写多了,也会有一些冗余代理。需要我们进一步优化,这里我们使用模板方法模式来对这个代理类进行优化,如下:
public abstract class BaseProxy implements InvocationHandler {
private Object obj;
protected BaseProxy(Object obj)
{
this.obj = obj;
}
public static Object getInstance(Object obj,InvocationHandler instance)
{
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),instance);
}
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
Object result;
try {
System.out.println("before method " + m.getName());
this.doBegin();
result = m.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getTargetException();
} catch (Exception e) {
throw new RuntimeException("unexpected invocation exception: "
+ e.getMessage());
} finally {
System.out.println("after method " + m.getName());
this.doAfter();
}
return result;
}
public abstract void doBegin();
public abstract void doAfter();
}
这样,代理的实现类只需要关注实现委派前的动作和委派后的动作就行,如下:
public class ProxyImpl extends BaseProxy {
protected ProxyImpl(Object o)
{
super(o);
}
public static Object getInstance(Object foo)
{
return getInstance(foo,new ProxyImpl(foo));
}
//委派前的动作
public void doBegin() {
// TODO Auto-generated method stub
System.out.println("begin doing....haha");
}
//委派后的动作
public void doAfter() {
// TODO Auto-generated method stub
System.out.println("after doing.....yeah");
}
}
从上面的代码,我们可以看出代理实现类的确是简单多了,只关注了委派前和委派后的动作,这是我们作为一个代理真正需要关心的。
至此,代理模式和动态代理已经告一段落。我们将动态代理引申一点说开去,来作为这篇文章的蛇足。
这个话题就是面向方面的编程,或者说AOP。我们看上面的ProxyImpl类,它的两个方法doBegin()和doAfter(),这是做核心动作之前和之后的两个截取段。正是这两个截取段,却是我们AOP的基础。在OOP里,doBegin(),核心动作,doAfter()这三个动作在多个类里始终在一起,但他们所要完成的逻辑却是不同的,如doBegin()可能做的是权限,在所有的类里它都做权限;而在每个类里核心动作却各不相同;doAfter()可能做的是日志,在所有的类里它都做日志。正是因为在所有的类里,doBegin()或doAfter()都做的是同样的逻辑,因此我们需要将它们提取出来,单独分析、设计和编码,这就是我们的AOP的思想。
这样说来,我们的动态代理就能作为实现AOP的基础了。好了,就说这么多,关于AOP技术,我们可以去关注关于这方面的知识

转载于:https://www.cnblogs.com/laskosoft/articles/1361438.html

代理模式、动态代理和面向方面相关推荐

  1. 【设计模式】代理模式 ( 动态代理 | 模拟 Java 虚拟机生成对应的 代理对象 类 )

    文章目录 前言 一.模拟 JVM 生成对应的 代理对象 二.模拟 JVM 生成对应的 代理对象 完整流程展示 1.目标对象接口 2.被代理对象 3.调用处理程序 4.模拟 JVM 生成的代理对象类 5 ...

  2. 红橙Darren视频笔记 代理模式 动态代理和静态代理

    红橙Darren视频笔记 代理模式 动态代理和静态代理(Android API 25) 关于代理模式我之前有过相关的介绍: https://blog.csdn.net/u011109881/artic ...

  3. Proxy 代理模式 动态代理 CGLIB

    代理的基本概念 几个英文单词: proxy [ˈprɒksi] n. 代理服务器:代表权:代理人,代替物:委托书: invoke [ɪnˈvəʊk] vt. 乞灵,祈求:提出或授引-以支持或证明:召鬼 ...

  4. 【设计模式】代理模式 ( 动态代理使用流程 | 创建目标对象 | 创建被代理对象 | 创建调用处理程序 | 动态创建代理对象 | 动态代理调用 )

    文章目录 前言 一.静态代理的弊端 二.动态代理的优势 三.动态代理使用流程 1.目标对象接口 2.被代理对象 3.调用处理程序 4.客户端 四.动态生成 代理对象 类 的 字节码 文件数据 前言 代 ...

  5. Java设计模式-之代理模式(动态代理)

    一.简介 1.什么叫代理模式:        简而言之就是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起 ...

  6. 【设计模式】代理模式 ( 动态代理 )

    文章目录 一.动态代理使用流程 二.动态代理代码示例 1.订单类 2.Service 接口 3.Service 实现类 4.Service 静态代理类 5.Dao 接口 6.Dao 实现类 7.Ser ...

  7. 代理模式(动态代理)

    动态代理.就是不用自己写代理类,只要实现接口就行了. 动态代理,这里介绍两种:jdk and cglib 第一个jdk //一个接口类 public interface Book{void read( ...

  8. Spring AOP中的静态代理和动态代理的原理和实践

    对于最近博主最近写博客的兴致大发,我也在思考:为什么而写博客?在互联网时代,无论你是牛人大咖,还是小白菜鸟,都有发表自己看法的权利.无论你是对的还是错的,都会在这个平台上找到答案.所以,我会尽可能去写 ...

  9. 【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  10. 浅谈Java和SAP ABAP的静态代理和动态代理,以及ABAP面向切面编程的尝试

    文章目录 Java的静态代理 静态代理的优缺点 ABAP的静态代理 Spring AOP的动态代理 JDK动态代理的优缺点 CGLIB动态代理的优缺点 ABAP CGLIB的模拟实现 ABAP Pre ...

最新文章

  1. 提高项目执行力的六板斧
  2. 无线传感器主要应用领域及发展趋势
  3. string.Format 指定字符串宽度
  4. java kafka搭建,Apache Kafka 安装步骤
  5. bzoj4172: 弹珠
  6. JAVA中Unicode输出_java输出全部unicode字符
  7. java后端英文_计算机程序员 前后台 英文怎么说
  8. 史上最全 IT 类学习资源
  9. Obsidian模板指北
  10. 大数据的处理之数据的抽取
  11. 二寸证件照尺寸怎么调?这两个方法让你在家也能制作证件照
  12. thonny python介绍_面向初学者的 Python IDE:Thonny
  13. javaweb之二维码
  14. DirectUI界面编程(零)简介
  15. BitConvert
  16. Python使用网易的SMTP发送邮件554问题的解决
  17. android:layout_alignParentLeft=true一下是什么意思
  18. Net Framework 4.7.2安装
  19. 火狐浏览器 : 获取安卓系统新版火狐浏览器(fenix).2021-02-19
  20. 机器视觉之ros人脸识别

热门文章

  1. LeetCode Maximum Product of Word Lengths(位操作)
  2. 前端登陆加密和后端解密
  3. 正则表达式基本语法元字符
  4. 牛客网 牛客练习赛13 A.幸运数字Ⅰ
  5. 基于SpringMVC下的Rest服务框架搭建【1、集成Swagger】
  6. flex swf和movieclip之前的微妙关系
  7. 马士兵java视频学习顺序
  8. 漫 谈oracle 中 的 空 值
  9. OpenCV中使用类VideoCapture加载视频和打开摄像头
  10. 利用OpenCV实现人眼的检测与跟踪