文章目录
- 什么是代理模式
- 一. 代理模式简介
- 二. 静态代理模式
- 三. 动态代理模式
- 万能模版
前言:笔记基于狂神设计模式视频、《大话设计模式》观后而写
(最近一直在更新之前的刷题博客,今天久违地更新一篇新博客啦~)
什么是代理模式
一. 代理模式简介
代理模式是很常用的设计模式噢~同时也有很多的类型,适当学习一下是非常有必要的!
- 定义:为其他对象提供一种代理,以控制对这个对象的访问。
可以理解为:在访问对象时引入一定程度的间接性,由这种间接性来附加多种用途。 - UML 图:代理组合真实角色,代理和真实对色都继承公共接口
- 举个例子:经纪人(代理)、明星(真实角色),都继承“接戏”接口。想找明星演戏的话,就得通过经纪人的“接戏”(把明星想象成经纪人的私有化对象)。这样做的话,经纪人可以在自己的“接戏”方法上添加筛选、谈薪等任务,而明星只需关注自己的业务即可。
- 在上面这个例子里,代理模式的好处就有体现出来了。真实角色更加地专一,并且在代理角色的拓展,也遵守了OOP原则的开放封闭原则。
- 代理模式的分类:
- 静态代理
- 动态代理(这两种类型,下文有结合代码使用、讲解)
- 远程代理:为一个对象在不同地址空间提供局部代表,用于隐藏一个对象存在于不同地址空间的事实(比如调用另一台PC的方法)
- 虚拟代理:用于存放实例化需要很长时间的真实对象,可以达到性能的最优化。比如打开哔哩哔哩,卡顿情况先显示文字,而图片、视频流可能就只是一个白框之类的玩意,之后再逐渐加载出来,这里就用到了虚拟代理
- 安全代理、智能指引等其他分类,感兴趣可以去查查~
二. 静态代理模式
- 角色分类:
- 抽象角色:接口 or 抽象类。
- 真实角色:就被代理的角色
- 代理角色:真实角色的代理,一般会加一些附属操作。
- 客户:访问代理角色的人。
- 好处:
- 使真实角色的操作更加纯粹,不用关注一些公共的业务。
- 公共业务交给代理角色,实现了业务的分工
- 公共业务发生拓展的时候,方便管理
- 缺点:一个真实角色对应一个代理角色,代码量增大,效率变低
- 样例代码:房东、中介、客户、租房(试着想想对应上面的什么角色吧~)
// ps:public class 不要介意,这里我是把不同文件的代码直接拷过来了。
// 1. 公共接口
public interface Rent {public void rent();
}// 2. 房东(真实角色)
public class Host implements Rent{@Overridepublic void rent() {System.out.println("房东:要出租房子");}
}// 3. 中介(代理角色)
public class MyProxy implements Rent {// 采用"组合"方式private Host host;MyProxy() {}MyProxy(Host host) {this.host = host;}@Overridepublic void rent() {// 可以添加一些更多的内容,而主体(被代理类)却不用关心这些事情,只需要做好自己的事即可。// 满足开放封闭原则System.out.println("中介:带着看房");host.rent();System.out.println("中介:签合同");}
}// 4. 客户:访问代理角色
public class Client {public static void main(String[] args) {// 只需要找中介即可,不用管房东Host host = new Host();MyProxy MyProxy = new MyProxy(host);MyProxy.rent();}
}
三. 动态代理模式
为了解决上面静态代理模式的缺点,这里又有了动态代理模式~
(这一块不太好理解,我写得估计也不太详细,建议再看一下视频、或者其他博客)
- 原理:动态代理基于反射机制实现。
- 和静态代理的区别:
- 静态代理:实现接口,再通过接口实现类的实例来代理。
- 动态代理:通过反射,造出一个接口类的实例。
(再原理一点,就是通过反射先造出一个带构造方法的,接口的克隆体,再通过这个克隆体的构造方法,生成接口实例)
- 使用到的类:
- Proxy:提供用于创建动态代理类和实例的静态方法,生成一个
- InvocationHandler:每个代理实例都具有一个关联的调用处理程序,调用代理实例的方法时,会被指派到其调用处理程序的 invoke 方法上。
- com.sun.proxy.$Proxy0 :由 Proxy::newProxyInstance() 生成的代理类。实现了传入接口的每一个方法,以及Object方法。并且统一调用了InvocationHandler 的 invoke() 方法。
- 好处:
- 静态代理的好处
- 一个动态代理类代理的是一个接口,一般就是对应的一类业务。规避了静态代理的缺点
- 在原始类和接口未知时,就确定代理类的代理行为。灵活。
- 样例代码:还是房东中介的例子噢
// 继承 InvocationHandler 接口,此时 this 就是一个 InvocationHandler
public class ProxyInvocationHandler implements InvocationHandler {// 被代理的接口private Rent rent;public void setRent(Rent rent) {this.rent = rent;}// 生成代理类public Object getMyProxy() {return Proxy.newProxyInstance(rent.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);}// InvocationHandler 对应的方法 invoke,用于处理代理实例,并且返回结果。被 $Proxy0 的方法调用@Overridepublic Object invoke(Object Proxy, Method method, Object[] args) throws Throwable {// Method 也是反射包下的类,和反射相关。System.out.println("动态代理来了噢!");Object res = method.invoke(rent, args);return res;}
}public class Client {public static void main(String[] args) {// 只需要找中介即可,不用管房东Host host = new Host();ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler();// 放入被代理对象proxyInvocationHandler.setRent(host);Rent proxy = (Rent)proxyInvocationHandler.getMyProxy();// 这里的 rent,之后会执行 invoke(此时参数 Method 就是 rent)// “代理对象”执行“接口方法”,然后指派到对应的 InvocationHandler 的 invoke 上proxy.rent();}
}
- 整理:无注释,一个文件不到30行,写完整个动态代理例子:
public interface Rent {void rent();
}public class RentDynamicProxy implements InvocationHandler {Rent rent;RentDynamicProxy(Rent rent) {this.rent = rent;}Object getProxy() {return Proxy.newProxyInstance(rent.getClass().getClassLoader(), rent.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("启用动态代理");return method.invoke(rent, args);}
}public class Host implements Rent{@Overridepublic void rent() {System.out.println("房东出租");}public static void main(String[] args) {Host host = new Host();RentDynamicProxy rentDynamicProxy = new RentDynamicProxy(host);Rent proxy = (Rent)rentDynamicProxy.getProxy();proxy.rent();}
}
万能模版
- 任何接口都可以用噢
public class BetterProxyInvocationHandler implements InvocationHandler {// 1. 被代理的接口private Object target;// 设置被代理的接口public void setTarget(Object target) {this.target = target;}// 2. 生成得到代理类public Object getProxy() {return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("执行了 " + method.getName() + " 方法");Object res = method.invoke(target, args);return res;}
}