【西瓜带你学设计模式 | 第九期 - 代理模式】代理模式 —— 静态与动态代理实现、优缺点与适用场景
文章目录前言1. 代理模式是什么2. 代理模式解决什么问题3. 代理模式的实现思路4. 静态代理Static Proxy4.1 场景示例给真实对象加“日志与权限”4.2 静态代理特点5. 动态代理JDK 动态代理5.1 定义接口与真实对象5.2 动态代理的核心InvocationHandler5.3 动态代理特点6. 常见类型与适用场景7. 优缺点7.1 代理模式优点7.2 代理模式缺点7.3 和装饰器模式的简单区分8. 总结前言在面向对象设计里“不直接动这个对象但要在访问它之前/之后做一些事”是非常常见的需求。代理模式Proxy Pattern就是用来解决这种问题的让一个对象充当“中介”把对另一个对象的访问过程包装起来。可以把它理解成想上门见客户但先经过前台/经纪人/中介——真正的客户对象不需要直接暴露给你。1. 代理模式是什么代理模式为其他对象提供一种代理以便控制对这个对象的访问。核心思想真实对象Real Subject负责“实际业务”代理对象Proxy负责“控制访问 增强行为”客户端Client面向代理编程而不是面向真实对象直接调用GoF 的经典结构里通常有Subject抽象主题接口/抽象类RealSubject真实主题核心实现Proxy代理主题增强与控制2. 代理模式解决什么问题代理模式主要用来实现这些能力权限控制/访问校验例如管理员才能调用某些方法。日志/埋点/审计例如统计方法耗时、记录调用参数与结果。远程调用封装例如RPC 框架里的 Stub/Client Proxy看起来像本地调用。缓存/延迟加载例如首次访问才创建真实对象后续直接命中缓存。增强与解耦例如不改真实对象代码也能叠加横切逻辑横切关注点。3. 代理模式的实现思路实现代理模式时常见套路是先定义统一接口Subject让RealSubject实现真实业务让Proxy内部持有一个RealSubject引用在Proxy的方法里访问前做增强权限/日志/计时再调用真实对象访问后做增强记录结果/清理资源/统计4. 静态代理Static Proxy4.1 场景示例给真实对象加“日志与权限”假设有一个接口ServicepublicinterfaceService{voiddoWork();}真实对象RealServicepublicclassRealServiceimplementsService{OverridepublicvoiddoWork(){System.out.println(RealService: 执行业务逻辑);}}代理对象ServiceProxypublicclassServiceProxyimplementsService{privatefinalServicereal;publicServiceProxy(Servicereal){this.realreal;}OverridepublicvoiddoWork(){// 1) 访问前增强权限校验System.out.println(Proxy: 权限校验通过示例);// 2) 访问前增强日志/计时longstartSystem.currentTimeMillis();// 3) 调用真实对象real.doWork();// 4) 访问后增强日志/统计longcostSystem.currentTimeMillis()-start;System.out.println(Proxy: doWork 耗时 costms);}}客户端使用代理publicclassClient{publicstaticvoidmain(String[]args){ServicerealnewRealService();ServiceproxynewServiceProxy(real);proxy.doWork();}}4.2 静态代理特点代理类由程序员手写或生成编译期就确定代理逻辑适合代理逻辑稳定、对象数量不太多的场景5. 动态代理JDK 动态代理当目标对象实现了接口可以用java.lang.reflect.Proxy做动态代理。5.1 定义接口与真实对象接口同上Service真实对象RealService5.2 动态代理的核心InvocationHandlerimportjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;publicclassJdkProxyFactory{SuppressWarnings(unchecked)publicstaticTTcreateProxy(Ttarget){return(T)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),newInvocationHandler(){OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println(JDK Proxy: 权限校验示例);longstartSystem.currentTimeMillis();Objectresultmethod.invoke(target,args);longcostSystem.currentTimeMillis()-start;System.out.println(JDK Proxy: method.getName() 耗时costms);returnresult;}});}}客户端publicclassClient{publicstaticvoidmain(String[]args){ServicerealnewRealService();ServiceproxyJdkProxyFactory.createProxy(real);proxy.doWork();}}5.3 动态代理特点代理逻辑在运行时决定通过反射调用代理类不需要手写每一种类型但性能上一般比静态代理略逊反射开销6. 常见类型与适用场景按“代理方式”可以分为静态代理手写代理类场景权限、日志、缓存等增强固定且简单JDK 动态代理目标必须实现接口场景框架通用增强例如 AOP、RPC 客户端代理CGLIB 动态代理目标类无需实现接口场景类代理Spring 某些配置下远程代理Remote Proxy客户端调用像本地一样但底层走网络典型RPC 的 client stub虚拟代理Virtual Proxy延迟初始化首次访问才创建真实对象场景大对象、图片/资源加载、懒加载保护代理Protection Proxy用于访问控制角色/令牌校验场景后台管理接口、敏感操作保护缓存代理Caching Proxy先查缓存命中则直接返回不命中再调用真实对象场景热点查询、幂等读7. 优缺点7.1 代理模式优点可控访问能在调用前后加逻辑权限、日志、审计增强解耦真实对象专注核心业务横切逻辑交给代理可扩展可以不断叠加新的代理增强7.2 代理模式缺点增加复杂度多一层对象、多一段调用链可能影响性能动态代理依赖反射/拦截不当使用会变“滥用中间层”过度代理导致调试困难7.3 和装饰器模式的简单区分装饰器通常强调“功能叠加”也会包一层对象但更偏向对象行为扩展代理更强调“控制访问/代表/访问管理”例如权限、远程、延迟在很多框架实现里两者可能“看起来很像”但意图和侧重点不同。8. 总结代理模式就是不直接调用真实对象而是先走一层“代表对象”在这里完成访问控制与增强然后再把请求转发给真实对象。