java设计模式之代理模式
概述
最近在看Spring Aop,而Spring Aop的底层就是通过代理模式实现的。所以想要看懂Spring底层源码,设计模式是必须要掌握的!
什么是代理模式呢?
答:一个类代表另一个类的功能,就是代理模式。
其原理图如下:
上图中,ProxyImage类是代理类,代理的类是RealImage,两者都实现了Image接口。用户如果想要访问RealImage的方法,只需要访问代理类就可以了!
代理模式分为静态代理与动态代理,下面我们分别对这两种代理进行讲解。
静态代理
测试
我们以实际生活中的租房为例,我们平时租房,一般都是联系中介,中介再找房东租房,最后中介再把从房东哪里租来的房子再租给我们。
我们分析一下上述案例,需要如下几个对象:
- 被代理对象:
HouseOwner
,房东 - 代理类:
Proxy
,中介 - 代理类与被代理类共同实现的接口:
Rent
- 客户端:
Tenant
,房客类
分析完之后,下面我们来编写我们的代码:
-
编写公共接口
Rent
package com.xdw; public interface Rent { void rent(); }
-
编写被代理类
HouseOwner
实现我们刚刚创建的Rent
接口package com.xdw; public class HouseOwner implements Rent { @Override public void rent() { System.out.println("房东出租房子!"); } }
-
编写代理类,同样需要继承
Rent
接口package com.xdw; public class Proxy implements Rent { // 设置一个类型为Rent的属性,方便我们调用被代理类的方法 private Rent rent; /** * 构造方法:将被代理类注入到代理类中 * @param rent */ public Proxy(Rent rent) { this.rent = rent; } @Override public void rent() { System.out.println("代理帮忙租房开始!"); // 房东租房 rent.rent(); System.out.println("代理帮忙租房结束!"); } }
-
编写房客类
Tenant
来调用代理类的Proxy方法package com.xdw; public class Tenant { public static void main(String[] args) { HouseOwner houseOwner = new HouseOwner(); Proxy proxy = new Proxy(houseOwner); proxy.rent(); } }
-
测试,运行
总结
看了上面的例子,可能有人会说,租房我直接找房东去租不就好了,中间加了一层代理,代码都比以前麻烦了,为什么要使用代理模式呢?
代理模式的好处:
- 可以是真实角色的操作更加纯粹!不用去关注一些公共的业务!
- 公共任务就交给了代理角色,实现了业务的分工
- 公共业务发生扩展的时候方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量会翻倍
静态代理有个致命的缺点,一个真实角色就需要一个代理,会导致我们的代码量翻倍,那么有什么方法能解决这个问题呢?
答:使用动态代理!
动态代理
- 动态代理与静态代理角色一样
- 动态代理的类是自动生成的,不是直接写好的
- 动态代理分为两大类:基于接口的动态代理;基于类的动态代理
- 基于接口: JDK的动态代理 【我们使用】
- 基于类: cglib
- java字节码实现:javassist
这里我们只用基于JDK的动态代理来进行测试!
测试
-
编写公共接口
Rent
package com.xdw; public interface Rent { void rent(); }
-
编写被代理类
HouseOwner
实现我们刚刚创建的Rent
接口package com.xdw; public class HouseOwner implements Rent { @Override public void rent() { System.out.println("房东出租房子!"); } }
-
编写代理类调用处理程序类,该类需要实现
InvocationHandler
接口package com.xdw; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyInvocationHandler implements InvocationHandler { private Object obj; public MyInvocationHandler(Object obj) { this.obj = obj; } /** * 动态获取代理类实例 * @return */ public Object getProxy() { return Proxy.newProxyInstance(this.getClass().getClassLoader(), obj.getClass().getInterfaces(), this); } /** * 利用反射执行代理类实例方法,可以在该方法中添加一些公共的逻辑,比如日志打印等 * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法被调用之前,可以编写一些逻辑"); Object result = method.invoke(obj,args); System.out.println("方法调用完毕,可以编写一些逻辑"); return result; } }
-
编写房客类
package com.xdw; public class Tenant { public static void main(String[] args) { HouseOwner houseOwner = new HouseOwner(); MyInvocationHandler handler = new MyInvocationHandler(houseOwner); // 获取代理对象 Rent proxy = (Rent)handler.getProxy(); proxy.rent(); } }
-
运行,测试
总结
- 利用JDK实现动态代理需要使用到
Proxy
类与InvocationHandler
接口 - 每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 2025年我用 Compose 写了一个 Todo App
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫