Java在java.lang.reflect包中有自己的代理支持,利用这个包我们可以在运行时动态地创建一个代理类,实现一个或多个接口,并将方法的调用转发到你所指定的类(继承了InvocationHandler的处理器类),因为实际的代理类是在运行时创建的,我们称这个java技术为:动态代理。在代码开始执行时,还没有proxy类,它是根据需要从我们传入的接口集创建的。

从Java1.2开始RMI可以利用reflection API直接将客户调用分派给远程服务,我们不需要真的产生skeleton。

到了Java5,连stub都不需要产生了,因为此时的RMI和动态代理搭配使用,动态代理动态产生stub,远程对象的stub是java.lang.reflect.Proxy实例,连同一个调用处理器,它是自动产生的,用来处理所有把客户的本地调用变成远程调用的细节。所以我们不再需要rmic。客户和远程对象的沟通的一切都在幕后处理掉了。

在本章,我们就利用java的动态代理创建一个保护代理。创建保护代理,我们必须使用Java API的动态代理。保护代理就是保护对象不要直接访问主题。保护代理可以控制在每一种情况下允许哪一种请求。
保护模式的类图:

类图中的代理包含两个类,一个是Proxy类,另一个是RealInvocationHandler类。其中Proxy类是由Java产生的,而且实现了完整的Subject接口。接口InvocationHandler也是java提供的,RealInvocationHandler实现了InvocationHandler接口,Proxy上的任何方法调用都会被传入此类。RealInvocationHandler控制对象RealSubject方法的访问

我们举一例子来说明保护代理模式。假如我们想控制一个博客的作者不能为自己的文章投票,其他人才可以投票,这样一个场景。我们用保护代理模式控制这两种人可以操作的权限。

第一步:我们设计一个ArticleBean。

接口ArticleBean:

public interface ArticleBean {String getAuthor();String getGender();String getArticleName();int getTicket();void setAuthor(String author);void setGender(String gender);void setArticleName(String articleName);void setTicket(int ticket);
}

实现ArticleBeanImpl:

package impl;import inter.ArticleBean;public class ArticleBeanImpl implements ArticleBean {private String author;private String gender;private String articleName;private int ticket = 0;@Overridepublic String getAuthor() {return author;}@Overridepublic String getGender() {return gender;}@Overridepublic String getArticleName() {return articleName;}@Overridepublic int getTicket() {return ticket;}@Overridepublic void setAuthor(String author) {this.author = author;}@Overridepublic void setGender(String gender) {this.gender = gender;}@Overridepublic void setArticleName(String articleName) {this.articleName = articleName;}@Overridepublic void setTicket(int ticket) {this.ticket = this.ticket + ticket;}
}

第二步:创建InvocationHandler

这里要创建两个处理器类。其中一个是OwnerInvocationHandler文章拥有者,在处理器中要阻止它调用setTicket()投票方法;另外一个是NonOwnerInvocationHandler投票人,在它的处理器中要阻止它调用除了setTicket方法外的set方法,这样是为了禁止投票人去修改文章的相关信息,投票人是没有权利这么做的。这些处理器类都要继承Java提供的InvocationHandler接口,实现其中的invoke方法,到时通过代理类Proxy调用的方法都会被传入处理器类中来。处理器类是我们唯一能直接访问到真实主题的地方。
注意:
invoke(Object proxy,Method method,Object[] args)的第一个参数proxy其实是没有用处的,因为它一直都是null,methd.invoke()方法,需要把原来的具体实现类作为参数传递进去,method.invoke(articleBean,args)相当于articleBean.method(args)。

OwnerInvocationHandler文章拥有者:

package impl;import inter.ArticleBean;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class OwnerInvocationHandler implements InvocationHandler {private ArticleBean articleBean;public OwnerInvocationHandler(ArticleBean articleBean){this.articleBean = articleBean;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if(method.getName().startsWith("get")){return method.invoke(articleBean,args);}else if(method.getName().startsWith("setTicket")){//作者不能投自己票System.out.println("不能给自己的文章投票!");throw new IllegalAccessException();}else if(method.getName().startsWith("set")){return method.invoke(articleBean,args);}}catch (Exception e){e.printStackTrace();}return null;}
}

NonOwnerInvocationHandler投票人:

package impl;import inter.ArticleBean;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class NonOwnerInvocationHandler implements InvocationHandler {private ArticleBean article;public NonOwnerInvocationHandler(ArticleBean article){this.article = article;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try{if(method.getName().startsWith("get")){return method.invoke(article,args);//对查询是全部提供的}else if(method.getName().startsWith("setTicket")){return method.invoke(article,args); //对投票方法是开放的}else if(method.getName().startsWith("set")){throw new IllegalAccessException(); //对于除作者以外的人,其他set方法是关闭的}}catch (Exception e){e.printStackTrace();}return null;}
}

第三步:创建Proxy类并实例化Proxy对象

获取文章拥有者的代理:

    //文章所有者的代理private ArticleBean getOwnerProxy(ArticleBean articleBean){ArticleBean ownerProxy = (ArticleBean)Proxy.newProxyInstance(articleBean.getClass().getClassLoader(),articleBean.getClass().getInterfaces(),new OwnerInvocationHandler(articleBean));return ownerProxy;}

获取投票人的代理:

//投票人的代理private ArticleBean getNonOwnerProxy(ArticleBean articleBean){ArticleBean nonOwnerProxy = (ArticleBean) Proxy.newProxyInstance(articleBean.getClass().getClassLoader(),articleBean.getClass().getInterfaces(),new NonOwnerInvocationHandler(articleBean));return nonOwnerProxy;}

通过代理来控制访问权限。Proxy代理可以访问的方法就是newProxyInstance方法的第二个参数传入的接口列表,具体调用哪个对象的这些接口就是第一个参数传入的对象,第三个参数是处理器类实例。往后,通过Proxy代理调用的方法都会被传入处理器类中。因此真正访问到真实主题的类就是处理器类。

第四步:测试。

import impl.ArticleBeanImpl;
import impl.NonOwnerInvocationHandler;
import impl.OwnerInvocationHandler;
import inter.ArticleBean;import java.lang.reflect.Proxy;
public class TestProtectionProxy {public static void main(String[] args) {TestProtectionProxy t = new TestProtectionProxy();t.test();}public void test(){ArticleBean articleBean = getActicle();  //获取一篇文章ArticleBean ownerProxy = getOwnerProxy(articleBean); //获取文章拥有者的代理ArticleBean nonOwnerProxy = getNonOwnerProxy(articleBean);获取投票人的代理//投票人开始投票try{nonOwnerProxy.setTicket(1);String articleN = nonOwnerProxy.getArticleName();int num = nonOwnerProxy.getTicket();String s = articleN +"已获得:" + num  + "票";System.out.println(s);}catch (Exception e){e.printStackTrace();}//文章拥有者投自己一票,会报错,因为不能投自己的try{ownerProxy.setTicket(1);}catch (Exception e){System.out.println("不能给自己的文章投票!");}}//文章所有者的代理private ArticleBean getOwnerProxy(ArticleBean articleBean){ArticleBean ownerProxy = (ArticleBean)Proxy.newProxyInstance(articleBean.getClass().getClassLoader(),articleBean.getClass().getInterfaces(),new OwnerInvocationHandler(articleBean));return ownerProxy;}//投票人的代理private ArticleBean getNonOwnerProxy(ArticleBean articleBean){ArticleBean nonOwnerProxy =  (ArticleBean) Proxy.newProxyInstance(articleBean.getClass().getClassLoader(),articleBean.getClass().getInterfaces(),new NonOwnerInvocationHandler(articleBean));return nonOwnerProxy;}//测试需要private ArticleBean getActicle(){ArticleBean bean = new ArticleBeanImpl();bean.setArticleName("论人文精神的重要性");bean.setAuthor("Wongkyunban");bean.setGender("Boy");bean.setTicket(0);return bean;}
}

测试结果:

论人文精神的重要性已获得:1票
不能给自己的文章投票!

最后给出Github上的demo代码。

谢谢阅读。

代理模式——保护代理(三)相关推荐

  1. 代理模式——远程代理(一)

    代理模式定义 为另一个对象提供一个替身或占位符以控制对这个对象的访问.使用代理模式创建代表对象,让代表对象控制对某对象的访问,被代理的对象可是远程的对象.创建开销大的对象或需要安全控制的对象. 代理分 ...

  2. 代理模式——虚拟代理(二)

    代理模式定义 为另一个对象提供一个替身或占位符以控制对这个对象的访问.使用代理模式创建代表对象,让代表对象控制对某对象的访问,被代理的对象可是远程的对象.创建开销大的对象或需要安全控制的对象. 代理分 ...

  3. 第六周 Java语法总结_设计原则_工厂模式_单例模式_代理模式(静态代理_动态代理)_递归_IO流_网络编程(UDP_TCP)_反射_数据库

    文章目录 20.设计原则 1.工厂模式 2.单例模式 1)饿汉式 2)懒汉式 3.Runtime类 4.代理模式 1)静态代理 2)动态代理 动态代理模板 21.递归 22.IO流 1.File 2. ...

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

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

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

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

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

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

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

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

  8. 代理模式——静态代理,动态代理(JDK代理和CGLib代理)

    概述 由于某些原因需要给某对象提供一个代理以控制对该对象的访问. 这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介. Java中的代理按照代理类生成时机不同又分为 ...

  9. [设计模式] - 代理模式(静态代理与动态代理)

    文章目录 一.代理模式简介 1. 什么是代理模式 2. 简单举例 二.代理模式的设计思路 1. 代理模式的构成 1. 静态代理 2. 动态代理 (1)接口代理 (2)Cglib代理 三. 代理模式总结 ...

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

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

最新文章

  1. php insert failed,较大的MySQL INSERT语句导致PHP错误
  2. 百度android 测试平台,集成百度活体检测(Android、iOS)
  3. 又一个创业者自杀:心生郁结,被曝曾卖房给员工发工资
  4. 基于GPU加速全局紧耦合的激光-IMU融合SLAM算法(ICRA2022)
  5. python可以在线编程吗-有哪些 python 的在线练习题或编程挑战的网站?
  6. java中实现对list的模糊查询
  7. 语音的基本概念--译自CMU sphinx
  8. 2019年1月数据库流行度排行: PostgreSQL蝉联2018年度冠军
  9. Directx11教程39 纹理映射(9)
  10. 使用 jdbc 从数据库中查询数据
  11. atitit.j2ee 1.5 1.6 的不同跟 Servlet 3.0新特性总结
  12. oracle中文转全拼音,oracle汉字转拼音
  13. 如何使用手机裁剪图片大小?
  14. 百度翻译API教程(完整Android代码)
  15. java 字节码 机器码_Java 执行引擎(从字节码到机器码)
  16. Angular启动项目时报错
  17. <read papers>学术论文的基金项目和研究成果格式怎么标注?
  18. Win10下搭建burpsuite pro,最详细的安装步骤(附Burpsuite pro安装包)
  19. garch dcc用matlab,MRS DCC GARCH 模型的MATLAB 程序修改
  20. 网络编程0:网络基础知识

热门文章

  1. python代码 练习3:空气质量查询工具
  2. 《统计学基于R》第一章 数据与R
  3. html做一个条码扫描页面,vue h5页面如何实现扫一扫功能,扫条形码获取编码
  4. Begging_Rust(译):做算术(第二章)
  5. 如何获取到微信公众号的网址
  6. [市场前景]无线流媒体:无线运营商潜在的金矿?(转载)
  7. 210_Python+OpenCV_04_模糊(均值、中值、高斯)
  8. 小码哥java一期 百度云_小码哥IOS 十一期
  9. 赛迪报告:除了“会呼吸”的肺,这些也能用3D打印实现!
  10. 树莓派开发板Android Things镜像烧录