软件设计模式之代理模式(上)

貌似停笔了近半个月了,实在不该啊,新的一年,对自己抱有新的期许,学习不能断,时刻让自己归零。

Back To Zero,就从这篇文章拉开今年的序幕吧!!

这篇文章准备分成2部分来写

第一部分介绍下有关代理模式的基本概念和静态代理、动态代理的优缺点及使用方法(包括扩展包CGLIB)

第二部分准备讲下如何自定义的去实现代理,不依赖于JDK给我们提供的代理类

代理模式,又称委托模式,顾名思义委托某物去办某事。

举个生活中的例子,临近大年了,在外地学习工作的小伙伴们也都开始购买回家的火车票,先不说网上订票,靠近火车站的小伙伴们自然很方便的可以到车站里去直接购票,那么如果是住郊外远离火车站的小伙伴们那该怎么办呢?当然也是可以专门搭车到火车站去买票,不过实为麻烦,这时候就衍生出一个东西——火车票代售处,没错,有了它,让我们这些远离火车站的小伙伴们也可以很轻松的购买到火车票。

这里的火车票代售其实就是一种代理(委托),火车票代售处代理了火车站售票,然而我们就可以在其中做一些我们想要的操作,比如权限的控制,代售处是不具备有退票的权限的。

好了,言归正传,先来看看代理的概念

1、代理概念 
为某个对象提供一个代理,以控制对这个对象的访问。 代理类和被代理类有共同的父类或父接口,这样在任何使用被代理类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给被代理类处理、以及被代理类执行完请求后的后续处理。

来一张UML图:

从图中可以看出,代理接口(Subject)、代理类(ProxySubject)、被代理类(RealSubject)形成一个"品"字结构。

代理类和被代理类都同时实现了代理接口,然后代理类通过引用被代理类的对象,来处理用户的访问请求。

这边根据代理类的生成时间又分成了2种代理,一种是静态代理,一种是动态代理。

文绉绉的理论概念让人看的难受,下面直接上实例,帮助大家理解。

2、静态代理

由开发人员创建或某工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,在代理类和被代理类运行前就确定了。

这里模拟一个计时器操作,记录处理业务的时间,下面直接上代码:

首先是代理接口,负责声明业务方法,代理类和被代理类都必须实现它。

 1 package com.lcw.proxy.test;
 2
 3 /**
 4  *
 5  * 业务接口(代理接口)
 6  *
 7  */
 8 public interface ITask {
 9
10     public void doTask(String taskName);
11
12 }

接下来是被代理类,这个类实现了代理接口,并执行主要的业务逻辑操作。

 1 package com.lcw.proxy.test;
 2
 3 import java.util.Random;
 4
 5 /**
 6  *
 7  * 业务处理类,处理主要的业务(被代理类)
 8  *
 9  */
10 public class DealTask implements ITask {
11
12     @Override
13     public void doTask(String taskName) {
14         try {
15             System.out.println("执行任务:" + taskName);
16             Thread.sleep(new Random().nextInt(1000));// 随机休眠一段时间
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20     }
21
22 }

再来是代理类,它同样的实现了代理接口,这样一来代理类和被代理类的对象既被"同化",通过构造器可以对被代理类的对象进行引用,从而达到可以访问被代理类的主要业务方法。

 1 package com.lcw.proxy.test;
 2
 3
 4 /**
 5  *
 6  * 代理类,需要实现和被代理类同一接口
 7  * 利用构造方法引用一个被代理类的对象为己持有
 8  *
 9  */
10 public class ProxyTask implements ITask {
11
12     private ITask dealTask;
13
14     public ProxyTask(ITask dealTask){
15         this.dealTask=dealTask;//引用一个被代理类的对象
16     }
17
18     @Override
19     public void doTask(String taskName) {
20
21         long startTime=System.currentTimeMillis();//记录业务起始时间
22         dealTask.doTask(taskName);//执行被代理类的主要业务
23         long endTime=System.currentTimeMillis();//记录业务结束时间
24         System.out.println("本次任务执行时间为:"+(endTime-startTime+"毫秒"));
25
26     }
27
28 }

然后来个测试类。

 1 package com.lcw.proxy.test;
 2
 3
 4 public class ProxyTest {
 5
 6     /**
 7      * 测试类
 8      */
 9     public static void main(String[] args) {
10          //静态代理
11          ITask dealTask=new DealTask();//获取被代理类的对象
12          ITask proxyTask=new ProxyTask(dealTask);//将被代理类的对象注入,获取静态代理类对象
13          proxyTask.doTask("打喷嚏..");
14
15     }
16 }

看下运行效果:

到这里,静态代理已经实现了,先来看看静态代理的优点, 被代理类只需要去关注主要的业务实现,其余操作比如日志记录,计时,权限控制等都可以交给代理类去额外的处理。

不过,这样子的实现方式,真的没有问题吗?

假如我们现在要为很多个业务方法都实现计时功能,如果这些方法分布在不同的类,那么是不是要为每一个类再单独的去创建代理类,长久下来,类体积会膨胀会爆炸的。

我们退一步想,假如我们现在额外的要扩展业务,在代理接口里添加新的业务方法,那么除了被代理类要去实现这个方法以外,是不是所有的代理类也都要去额外的添加实现这个方法,所谓的"牵一发而动全身",需要额外的添加大量的重复代码,这是违背软件设计原则的。

还有就是代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

为解决这个问题,我们的动态代理就要出场了。

3、JDK动态代理

JDK动态代理类的源码是在程序运行期间由虚拟机JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。在代理类和被代理类程序运行时确定。

在Java中要想实现动态代理机制,需要java.lang.reflect.InvocationHandler接口和 java.lang.reflect.Proxy类的支持。

来看下Java API里的主要方法介绍(具体自己去翻阅哈)

java.lang.reflect.Proxy

java.lang.reflect.InvocationHandler 

这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对被代理类的代理访问。

动态代理实现步骤

1、实现InvocationHandler接口创建自己的调用处理器
2、给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
3、以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
4、以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象

好了,再说下去有的朋友要开始混乱了,直接看实例,整理下思路

依旧是上面的代理接口,这里我们需要创建一个自己的调用处理器(实现InvocationHandler接口)

 1 package com.lcw.proxy.test;
 2
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5
 6 /**
 7  *
 8  * 动态代理类 通过实现 InvocationHandler 接口创建自己的调用处理器
 9  *
10  */
11 public class JDKProxy implements InvocationHandler {
12
13     private Object dealTask;
14
15     public JDKProxy(Object dealTask) {
16         this.dealTask = dealTask;// 引用被代理类的对象
17     }
18
19     /**
20      * 参数: proxy为代理类的对象 method为被代理类的方法 args为该方法的参数数组
21      *
22      */
23     @Override
24     public Object invoke(Object proxy, Method method, Object[] args)
25             throws Throwable {
26         // 调用实现业务逻辑方法,并实现附属功能(日志,权限判断等)
27         // 利用反射机制将请求分派给被代理类处理
28
29         long startTime = System.currentTimeMillis();// 记录业务起始时间
30         // invoke方法参数sub是实际的被代理对象,args为执行被代理对象相应操作所需的参数
31         method.invoke(dealTask, args);// 返回值是一个Object对象也是调用被代理类方法的返回值(这里只是实现计时操作,所以返回值为null)
32         long endTime = System.currentTimeMillis();// 记录业务结束时间
33         System.out.println("本次任务执行时间为:" + (endTime - startTime + "毫秒"));
34         return null;
35     }
36
37 }

接着是测试类

 1 package com.lcw.proxy.test;
 2
 3 import java.lang.reflect.Proxy;
 4
 5 public class ProxyTest {
 6
 7     /**
 8      * 测试类
 9      */
10     public static void main(String[] args) {
11         // //静态代理
12         // ITask dealTask=new DealTask();//获取被代理类的对象
13         // ITask proxyTask=new ProxyTask(dealTask);//获取静态代理类对象
14         // proxyTask.doTask("打喷嚏..");
15
16         // 动态代理
17         ITask dealTask = new DealTask();// 获取被代理类的对象
18         JDKProxy jdkProxy = new JDKProxy(dealTask);// 获取动态代理类对象
19         /**
20          * loader 类加载器 interfaces 实现接口 h InvocationHandler
21          */
22         ITask iTask = (ITask) Proxy.newProxyInstance(dealTask.getClass()
23                 .getClassLoader(), dealTask.getClass().getInterfaces(),
24                 jdkProxy);
25         iTask.doTask("在发呆...");
26     }
27
28 }

看下实现效果:

动态代理的优缺点:
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。在本示例中看不出来,因为invoke方法体内嵌入了具体的外围业务(记录任务处理前后时间并计算时间差),实际中可以类似Spring AOP那样配置外围业务。动态代理的应用使我们的类职责更加单一,复用性更强。

美中不足:
Proxy已经设计得非常优美,但是还是有一点点小小的遗憾之处,那就是它始终无法摆脱仅支持 interface代理的桎梏,因为它的设计注定了这个遗憾。JDK动态代理只能为实现接口的类进行代理,没有实现接口的类无法代理。

有问题就会想去解决,所以此时Cglib要登场了。

4、Cglib动态代理:

Cglib的介绍:

代理为控制要访问的目标对象提供了一种途径。当访问对象时,它引入了一个间接的层。JDK自从1.3版本开始,就引入了动态代理,并且经常被用来动态地创建代理。JDK的动态代理用起来非常简单,但它有一个限制,就是使用动态代理的对象必须实现一个或多个接口。如果想代理没有实现接口的继承的类,该怎么办?现在我们可以使用Cglib包。

Cglib是针对类来实现代理的,实现原理是为被代理类产生一个子类,通过拦截技术拦截父类方法的调用。

来看个小例子:

首先是一个业务逻辑类

 1 package com.lcw.proxy.test;
 2
 3 import java.util.Random;
 4
 5 public class Task {
 6
 7     public void doTask(){
 8         System.out.println("我在完成任务");
 9         try {
10             Thread.sleep(new Random().nextInt(1000));
11         } catch (InterruptedException e) {
12             e.printStackTrace();
13         }
14     }
15
16 }

再来一个Cglib代理类,提供代理对象,这里需要实现MethodInterceptor接口

 1 package com.lcw.proxy.test;
 2
 3 import java.lang.reflect.Method;
 4
 5 import net.sf.cglib.proxy.Enhancer;
 6 import net.sf.cglib.proxy.MethodInterceptor;
 7 import net.sf.cglib.proxy.MethodProxy;
 8
 9 /**
10  *
11  * 创建Cglib的代理类(需要实现MethodInterceptor接口)
12  *
13  */
14 public class CglibProxy implements MethodInterceptor{
15
16     //需要向客户端返回一个代理类对象
17     private Enhancer enhancer=new  Enhancer();
18
19     public Object getProxy(Class c){
20         //cglib代理的原理就是为被代理类(父类)创建子类
21         enhancer.setSuperclass(c);
22         enhancer.setCallback(this);
23         return enhancer.create();
24     }
25
26     /**
27      * 参数:
28      * obj被代理类的对象
29      * method被代理类的方法
30      * args被代理类方法的参数
31      * proxy代理类对象
32      */
33     @Override
34     public Object intercept(Object obj, Method method, Object[] args,
35             MethodProxy proxy) throws Throwable {
36         long startTime=System.currentTimeMillis();//记录业务起始时间
37         proxy.invokeSuper(obj, args);//cglib代理的原理就是为被代理类(父类)创建子类,所以这边调用了父类super,参数一:父类的对象、参数二:方法参数
38         long endTime=System.currentTimeMillis();//记录业务结束时间
39         System.out.println("本次任务执行时间为:"+(endTime-startTime+"毫秒"));
40         return null;
41     }
42
43 }

写个测试类试试

 1 package com.lcw.proxy.test;
 2
 3 import java.lang.reflect.Proxy;
 4
 5 public class ProxyTest {
 6
 7     /**
 8      * 测试类
 9      */
10     public static void main(String[] args) {
11         // //静态代理
12         // ITask dealTask=new DealTask();//获取被代理类的对象
13         // ITask proxyTask=new ProxyTask(dealTask);//获取静态代理类对象
14         // proxyTask.doTask("打喷嚏..");
15
16         // 动态代理
17 //        ITask dealTask = new DealTask();// 获取被代理类的对象
18 //        JDKProxy jdkProxy = new JDKProxy(dealTask);// 获取动态代理类对象
19 //        /**
20 //         * loader 类加载器 interfaces 实现接口 h InvocationHandler
21 //         */
22 //        ITask iTask = (ITask) Proxy.newProxyInstance(dealTask.getClass()
23 //                .getClassLoader(), dealTask.getClass().getInterfaces(),
24 //                jdkProxy);
25 //        iTask.doTask("在发呆...");
26
27         //Cglib代理
28         CglibProxy cglibProxy=new CglibProxy();
29         Task task=(Task) cglibProxy.getProxy(Task.class);
30         task.doTask();
31
32     }
33
34 }

运行效果:

作者:Balla_兔子
出处:http://www.cnblogs.com/lichenwei/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
正在看本人博客的这位童鞋,我看你气度不凡,谈吐间隐隐有王者之气,日后必有一番作为!旁边有“推荐”二字,你就顺手把它点了吧,相得准,我分文不收;相不准,你也好回来找我!

时间: 2024-08-30 02:07:47

软件设计模式之代理模式(上)的相关文章

设计模式之代理模式(Proxy)摘录

23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而一个对象创建型模式将实例化委托给另一个对象.创建型模式有两个不断出现的主旋律.第一,它们都将关于该系统使用哪些具体的类的信息封装起来.第二,它们隐藏了这些类的实例是如何被创建和放在一起的.整个系统关于这些对象所知道的是由抽象类所定义的接口.因此,创建型模式在什么被创建,谁创建它,它是怎样被创建的,以

C#设计模式(13)——代理模式(Proxy Pattern)

一.引言 在软件开发过程中,有些对象有时候会由于网络或其他的障碍,以至于不能够或者不能直接访问到这些对象,如果直接访问对象给系统带来不必要的复杂性,这时候可以在客户端和目标对象之间增加一层中间层,让代理对象代替目标对象,然后客户端只需要访问代理对象,由代理对象去帮我们去请求目标对象并返回结果给客户端,这样的一个解决思路就是今天要介绍的代理模式. 二.代理模式的详细介绍 代理模式按照使用目的可以分为以下几种: 远程(Remote)代理:为一个位于不同的地址空间的对象提供一个局域代表对象.这个不同的

java设计模式6——代理模式

java设计模式6--代理模式 1.代理模式介绍: 1.1.为什么要学习代理模式?因为这就是Spring Aop的底层!(SpringAop 和 SpringMvc) 1.2.代理模式的分类: 静态代理 动态代理 1.3.代理模式关系图(以租房子为例) 2.静态代理 2.1.角色分析: 抽象角色:一般会使用接口或者抽象类来解决 真实角色:被代理的角色 代理客户:代理真实角色.代理真实角色后,我们一般会做一些附属的操作 客户:访问代理对象的人 2.2.例1(租房子演示) 2.2.1.抽象角色实现(

Android设计模式之代理模式 Proxy

一.概述 代理模式也是平时比较常用的设计模式之一,代理模式其实就是提供了一个新的对象,实现了对真实对象的操作,或成为真实对象的替身.在日常生活中也是很常见的.例如A要租房,为了省麻烦A会去找中介,中介会替代A去筛选房子,A坐享中介筛选的结果,并且交房租也是交给中介,这就是一个典型的日常生活中代理模式的应用.平时打开网页,最先开到的一般都是文字,而图片等一些大的资源都会延迟加载,这里也是使用了代理模式. 代理模式的组成: Abstract Subject:抽象主题-声明真实主题和代理主题共同的接口

【设计模式】代理模式

代理模式在所需对象和用户代码之间增加了一层对象,这个对象被称为代理.用户代码只需要直接操作代理对象即可.著名的代理模式的例子就是引用计数指针对象,它使得我们对真实对象的操作都需要经过引用计数指针对象.下面是用C++写的一个运用了代理模式的例子. #include <iostream> #include <string> using namespace std; // 作为接口的抽象基类 class Subject { public: virtual void DoAction()

设计模式之代理模式20170724

结构型设计模式之代理模式: 一.含义 代理模式也叫做委托模式,其定义如下: 为其他对象提供一种代理以控制对这个对象的访问. 二.代码说明 1.主要有两个角色 1)具体主题角色 也叫做委托角色.被代理角色.它是业务逻辑的具体执行者. 2)代理主题角色 也叫做委托类.代理类.它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后处理工作. 一个代理类可以代理多个被委托者或被代理者. 2.在用C实现过程中也是参考这种思想,以游戏代理场

php设计模式之代理模式

代理模式[PROXY PATTERN] <?php header("Content-type: text/html; charset=UTF-8"); /** * 什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧, * 那代理人总要知道被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口, * 代理人虽然不能干活,但是被代理的人能干活呀. */ // 比如西门庆找潘金莲,那潘金莲不好意思答复呀,咋办,找那个王婆做代理,表现在程序上时这样的: //

设计模式之代理模式(3)

观看这篇文章前,请先阅读设计模式之代理模式(1) 静态代理会发生类爆炸?那jdk的使用的动态代理到底是怎么做到的呢?我来大概模拟一下jdk的动态代理. 这是我的目录结构:(可先跳过代码,到最下面听下我的BB,在对照代码来看!) 我先来介绍一下这些兄弟: Tank: package cn.asto.proxy; import java.util.Random; public class Tank implements Moveable{ public void move(){ System.out

设计模式之代理模式之读写分离!!!

小伙伴们你们的小可爱逗比又上线了!!! 最近感觉带表情的文章看多了,写篇文章不放上几十个表情感觉自己都写不出来什么!!!原谅你们的小可爱放荡...不羁...爱谁谁!!! 好了好了,开始上课了,今天你们的刘老师要讲的内容是?对对对!!!最后面那个同学说的对,就是设计模式之代理模式额外加读写分离设计!下课别走啊,刚才哪位同学!!! 代理这个词,相信很多同学应该不陌生了吧! 1.从非程序来说,代理生活中最常见的就是朋友圈刷屏的各种商品等等. 2.从服务器架构上来说,代理就是转发,好比你和第三者之间需要