代理是指代表委托人管理,以他人的名义,代表委托人处理超出自己职责或力所能及的事务,达成合作关系并促成更高效地完成事务。 。例如,名人经纪人不知道如何像名人一样唱歌、跳舞或表演。相反,他们为明星处理一些自己无暇顾及的事务(这并不意味着他们可以代理非常事情),比如促销宣传、合同谈判等。达成合约后,他们会通知明星演出。另一个例子是机票销售代理商既不制造飞机也不提供飞行服务。他们只负责卖票。律师不会因胜诉而获得赔偿,也不会因败诉而受到法律制裁。他们只负责代表人打官司等等。 图片来源网络,侵权删除 生活中还有很多这样的例子,但对于我们这些从事技术工作的人来说,我相信以网络代理为例是最合适的。 首先,我们在能够接入互联网之前,必须先到互联网服务提供商(ISP)申请互联网宽带服务,所以顺理成章的获得光纤入户并获得调制解调器,也就是俗称的调制解调器。作为“猫”。好了,“猫”实现了上网接口,看代码。 1public interface Internet{//上网接口 2 3 public void access(String url); 4 5} 1public类Modem实现Internet {//Modem 2 3 @覆盖 4 public void access(String url){//实现上网接口 5 System.out.println("访问:" + url); 6 } 7} 作为调制解调器,它必须具有互联网接入功能。用户的电脑只需要用网线连接这只“猫”就可以上网。真有这么简单吗?然而有一天,我们发现孩子们总是在学习的时候偷偷上网看电影、玩游戏,于是我们决定对某些网站进行过滤,防止色情、赌博、毒品对未成年人的伤害。然后,我们需要在客户端电脑和猫之间添加一层代理来过滤一些不良网站。最后,我们决定购买一个带有过滤功能的路由器。 1public class RouterProxy实现Internet{//路由器代理类 2 3 private Internet modem;//持有代理类的引用4 private List blackList = Arrays.asList("电影", "游戏", "音乐", "小说"); 5 6 公共 RouterProxy() { 7 this.modem = new Modem();//实例化代理类 8  System.out.println("拨号上网...连接成功!"); 9 } 10 11 @覆盖 12 public void access(String url) {//同样实现上网接口方法 13 for (String keywords: blackList) {//循环黑名单 14 if (url.contains(keyword)) {//是否包含黑名单词 15    System.out.println("禁止访问:" + url); 16返回; 17} 18} 19    modem.access(url);//正常上网 20} 21} 注意,这里的路由器代理主要起到代理的作用。与之前的“猫”一样,它也实现了互联网接口。看似具有上网功能,其实不然。第12行代码从一开始就实现了对上网功能的过滤。如果该地址包含黑名单敏感词,将被禁止访问,直接退出。否则,第19行就会调用“猫”的上网方法,你看,最后调用的是“猫”的上网函数。注意,这里为了控制“猫”,特意为此创建了代理。我们直接在第7行实例化它,而不需要其他人来注入它。好了,孩子现在已经上线了,迫不及待地想运行一下。 1公共类客户端{ 2 公共静态无效主(字符串[] args){ 3 互联网代理 = new RouterProxy(); //代理被实例化 4  proxy.access("http://www.movi​​www.hack95.com");5  proxy.access("http://www.hack95.com"); 6  proxy.access("ftp://www.学.com/java"); 7  proxy.access("http://www.hack95.com"); 8 9           /* 运算结果 10 拨号上网...连接成功! 11 禁止访问:http://www.movi​​www.hack95.com 12 禁止访问:http://www.hack95.com 13 访问:ftp://www.hack95.com/java 14 访问:http://www.hack95.com 15*/ 16} 17} 在第 3 行中,子进程不再被实例化为“cat”,而是被路由器代理所取代。也就是说,每个在线的人都是连接到路由器上的,而不是直接连接到“猫”上的。这不仅为我们省去了拨号的麻烦(路由器帮助拨号),而且孩子们也无法再访问杂乱的网站。事实上,这个代理本身并不具备访问互联网的能力。它只是调用“猫”上网功能。它存在的目的只是为了控制“猫”的互联网访问并充当它的代理。 说到这里,大家有没有发现,这种代理模式和装饰器模式很相似呢?如果你观察UML类图之间的关系,你会发现它们几乎是一模一样的。那么这个模式有什么意义呢?事实上,代理模式更强调对代理对象的控制,而不是局限于装饰目标对象、增强其原有功能。就像明星的例子一样,如果钱不够,合同达不成,明星就不允许随意炫耀。 相信大家都有清楚的认识。这也是我们最常用的代理模式。其实还有一种叫做动态代理。不同的是,它的实例化过程是在运行时完成的,这意味着我们不需要专门为某个接口编写这样的代理类,而是根据接口动态生成的。 例如,让我们忘记之前的路由器代理。当我们内网的上网设备越来越多的时候,路由器的Lan口已经满了,不够用了,所以我们决定换成交换机,看一下代码。 1public interface Intranet {//LAN接入接口 2 3 public void fileAccess(字符串路径); 4 5}为了简单起见,我们假设这个交换机Switch实现了LAN访问接口Intranet。请注意,这不是互联网接口。 1public类Switch实现内网{ 2 3 @覆盖 4 公共无效文件访问(字符串路径){ 5 System.out.println("访问内网:" + 路径); 6 } 7 8} 这里发生的是局域网文件访问,例如在另一台内网机器上复制共享文件,我们要保证和以前一样的关键字过滤控制功能,也就是说,无论是哪个地址,都必须首先进行过滤。如何重复利用? 我们在这里想一下。猫实现互联网接入接口,交换机实现局域网接入接口。那么我们应该如何编写我们的过滤代理类呢?是实现Internet接口还是内网接口?或者两者都做?当添加新的类接口时,我们是否需要不断更改实现类?这显然是行不通的。过滤器无非是一段过滤逻辑,不需要来回改变。这违反了设计模式的开闭原则。动态代理应运而生,我们看一下代码。 1public class KeywordFilter 实现 InvocableHandler { 2 3 private List blackList = Arrays.asList("电影", "游戏", "音乐", "小说"); 4 5 // 被代理的真实对象,无论是猫、开关还是其他东西。 6 私有对象起源; 7 8 公共关键字过滤器(对象起源){ 9 this.origin = origin;//注入代理对象 10 System.out.println("开启关键字过滤模式..."); 11} 12 13 @覆盖 14 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 15 //业务逻辑切入方法方面之前16 字符串arg = args[0].toString(); 17 for (字符串关键字:blackList) { 18    if (arg.toString().contains(关键字)) { 19           System.out.println("禁止访问:" + arg); 20                                                                     返回 null; 21 } 22 } 23          // 调用真正的代理对象方法 24 return 方法.invoke(origin, arg); 25} 26 27} 对于这个关键字过滤功能,我们不再写到代理类中,而是另外写一个类,实现JDK反射包中提供的InvocableHandler接口,并在第9行注入要代理的对象,不管是猫还是猫转变。始终是一个Object,然后在第14行实现了invoke调用方法,以后会调用生成的动态代理来运行这个逻辑。显然,在执行真正的对象方法之前,我们在这里仍然保持相同的逻辑。运行过滤逻辑来控制。由于传入的参数是代理对象的method方法和一堆参数args,所以请注意,这里第24行我们必须使用反射来调用代理对象的origin。最后,让我们看看如何运行它。 1公共类客户端{ 2 公共静态无效主(字符串[] args){ 3 4 //访问外网(Internet),生成猫代理。 5 互联网 互联网 = (互联网) Proxy.newProxyInstance( 6            Modem.class.getClassLoader(), 7 Modem.class.getInterfaces(), 8              new KeywordFilter(new Modem()));9 internet.access(“http://www.movi​​www.hack95.com”); 10 internet.access("http://www.hack95.com"); 11 互联网.access(“http://www.hack95.com”); 12 Internet.access(“http://www.hack95.com”); 13 14         //访问内网(LAN)并生成交换机代理。 15 内网内网=(内网)Proxy.newProxyInstance( 16     Switch.class.getClassLoader(), 17 Switch.class.getInterfaces(), 18               new KeywordFilter(new Switch())); 19        Intranet.fileAccess("\\192.68.1.2\Shared\Movies\www.hack95.com4"); 20 Intranet.fileAccess("\\192.68.1.2\Shared\Game\Hero.exe"); 21 Intranet.fileAccess("\\192.68.1.4\shared\Java学习资料.zip"); 22 Intranet.fileAccess("\\192.68.1.6\Java知音\设计模式.doc是什么鬼"); 23 24 /* 25             开启关键字过滤模式... 26 禁止访问:http://www.movi​​www.hack95.com 27 禁止访问:http://www.hack95.com 28 访问:http://www.hack95.com 29 访问:http://www.hack95.com 30 开启关键字过滤模式...31 禁止访问:\192.68.1.2 共享电影 www.hack95.com4 32 禁止访问:\192.68.1.2 共享游戏 Hero.exe 33 访问内网:\192.68.1.4sharedJava学习资料.zip 34 访问内网:\192.68.1.6Java知音设计模式是什么鬼.doc 35 36*/ 37} 38} 可以看到,无论我们是访问互联网还是局域网,我们只需要生成对应的代理并调用即可,执行相同的过滤逻辑。这样我们就不需要再写任何代理类了。我们只需要实现一次 IncationHandler 就可以一劳永逸了。代理可以在运行时动态生成,以达到兼容任意接口的目的。 事实上,动态代理模式在很多框架中都有广泛的应用,比如spring的面向切面的AOP。我们只需要定义一个切面类@Aspect,并声明它的入口点@Pointcut(被代理的对象的哪些方法,也就是这里的cat和switch的access和accessFile),以及切入的代码块(要添加的逻辑,比如这里的过滤功能代码,可以分为预执行@Before、后执行@After、异常处理@AfterThrowing等),所以框架自动为我们生成了agent并进行了裁剪进入目标执行。就像每个方法前后添加日志的例子,或者更经典的事务控制的例子,在所有业务代码之前切换到“事务启动”,执行完之后再切换到“事务提交”。如果抛出异常并被捕获,则执行“事务”。 “回滚”,这样就不需要在每个业务类中都写这些重复的代码了。一劳永逸,冗余代码大大减少,开发效率惊人提升。 图片来源网络,侵权删除 他没有耳朵去听窗外发生的事情,只读圣贤之书。毕竟天地如山,山外的事情还是交给专家吧。 版权声明:本文内容由网友自愿贡献,本文所表达的观点仅代表作者自己的观点。本网站仅提供信息存储空间服务,不拥有任何所有权,也不承担相关法律责任。如果您发现本站有任何涉嫌侵权/非法内容,请发送邮件举报。一经核实,该网站将立即删除。 本文由斑马博客整理。本文链接为:https://www.hack95.com/index.php/post/2403.html