目录
引言
一、概述
二、代理模式介绍
(一)静态代理
(二)动态代理
引言 一、概述
在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。
在软件设计中,使用代理模式的例子也很多,例如,要访问的远程对象比较大(如视频或大图像等),其下载要花很多时间。还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。
代理模式的定义:
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
代理模式的主要优点有:
代理模式会造成系统设计中类的数量增加
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
增加了系统的复杂度;
代理模式的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。
代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
简单的说就是,我们在访问实际对象时,是通过代理对象来访问的,代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
举个例子:
人到了适婚年龄,父母总是迫不及待希望早点抱孙子。而现在社会的人在各种压力之下,都选择晚婚晚育。于是着急的父母就开始到处为自己的子女相亲,比子女自己还着急。
这个相亲的过程,就是一种我们人人都有份的代理。来看代码实现:
定义一个接口(面向接口)
public interface Person { void findLove(); }
定义一个被代理类
public class Son implements Person { @Override public void findLove() { System.out.println("儿子要求:上海名媛"); } }
定义一个代理类
public class Father implements Person { private Son son; public Father(Son son) { this.son = son; } @Override public void findLove() { System.out.println("父母物色对象"); son.findLove(); System.out.println("双方同意交往,确立关系"); } }
测试类
public class Test { public static void main(String[] args) { Father father=new Father(new Son()); father.findLove(); } }
这就是一个简单的静态代理模式。
(二)动态代理代理类在程序运行时创建的代理方式被成为动态代理。
动态代理和静态对比基本思路是一致的,只不过动态代理功能更加强大,随着业务的扩展适应性更强。如果还以找对象为例,使用动态代理相当于是能够适应复杂的业务场景。
不仅仅只是父亲给儿子找对象,如果找对象这项业务发展成了一个产业,进而出现了媒婆、婚介所等这样的形式。那么,此时用静态代理成本就更大了,需要一个更加通用的解决方案,要满足任何单身人士找对象的需求。我们升级一下代码,先来看 JDK 实现方式:
public interface Person { void findLove(); }
public class Customer implements Person { @Override public void findLove() { System.out.println("上海名媛"); } }
public class JdkMeiPoInvocationHandler implements InvocationHandler { private Object target; public Object getInstance(Object target) { this.target = target; Class> clazz = target.getClass(); return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object obj = method.invoke(target, args); after(); return obj; } private void before(){ System.out.println("我是媒婆:我要给你找对象,现在已经拿到你的需求"); } private void after(){ System.out.println("如果合适的话,就准备办事"); } }
InvocationHandler接口
是proxy代理实例的调用处理程序实现的一个接口,每一个proxy代理实例都有一个关联的调用处理程序;在代理实例调用方法时,方法调用被编码分派到调用处理程序的invoke方法。
特别注意:
这一段代理代码,通用!!!我们需要改动的只是其中的一个对象。只要是动态代理类,都可以用这样一个被规范成工具类的代码!
private Object target;//target就是一个对象,可以改动,只可以改动它 public Object getInstance(Object target) { this.target = target;//要改动时这里也要改动,只改动target Class> clazz = target.getClass();//要改动时这里也要改动,只改动target return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //我们可以在代理类中定义额外的方法,然后在这里面调用 Object obj = method.invoke(target, args); return obj; }
public class JdkTest { public static void main(String[] args) { Person person= (Person) new JdkMeiPoInvocationHandler().getInstance(new Customer()); person.findLove(); } }
同样我们可以得到被代理者的儿子与媒婆代理之间,产生需求联系,然后得出被代理者的需求实现。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)