前言
代理模式为目标对象提供代理服务,客户端不直接操作目标对象,而是通过代理类管理对象,是不是有点类似Spring的IoC容器,没错!Spring等框架就是很多技术的融合,设计时融入了许多设计模式的思想。
1. 概念
代理模式是一种结构型设计模式,其主要目的是控制和管理访问对象。代理模式分为三种主要类型:静态代理、动态代理(JDK 动态代理和CGLIB 动态代理),以及虚拟代理。
2. 代理模式种类
-
静态代理:
静态代理是在编译阶段就已经确定代理关系,代理类和真实类的关系在代码中是固定的。通过手动编写代理类,在代理类中调用真实类的方法,并可以在方法调用前后进行一些额外的操作。
-
动态代理:
动态代理分为JDK动态代理和CGLIB动态代理。在运行时通过反射等机制动态创建代理类,无需在编译期确定代理关系,更加灵活。
-
虚拟代理:
虚拟代理是在访问对象时才创建真实对象,适用于资源开销较大的情况。虚拟代理可以延迟真实对象的创建,提高系统性能。
代理模式的优点包括:
代理模式的优缺点
代理模式的优点包括:
- 控制对真实对象的访问,可以在访问前后执行一些额外的操作。
- 隐藏目标对象的实现细节。
- 降低系统的耦合度。
代理模式的缺点包括:
- 由于每个代理都要实现一遍目标接口,会导致代码冗余。
- 增加了系统的复杂度。
根据实际需求和场景,选择不同类型的代理模式。
3.示例代码
-
静态代理:
在静态代理中,代理类是在编译期间就已经确定的,代理类和目标类的关系在代码中是固定的。// 接口 interface Subject { void request(); } // 真实对象 class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject handles request."); } } // 代理对象 class Proxy implements Subject { private RealSubject realSubject; public Proxy(RealSubject realSubject) { this.realSubject = realSubject; } @Override public void request() { // 额外的操作 System.out.println("Proxy handles request."); realSubject.request(); // 额外的操作 } }
-
动态代理(JDK动态代理):
动态代理是在运行时创建的代理对象,它不需要提前编写代理类,而是通过 Java 反射机制在运行时动态生成。import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; // 接口 interface Subject { void request(); } // 真实对象 class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject handles request."); } } // 动态代理处理器 class DynamicProxyHandler implements InvocationHandler { private Object realSubject; public DynamicProxyHandler(Object realSubject) { this.realSubject = realSubject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 额外的操作 System.out.println("DynamicProxy handles request."); Object result = method.invoke(realSubject, args); // 额外的操作 return result; } } // 客户端使用动态代理 public class DynamicProxyExample { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); InvocationHandler handler = new DynamicProxyHandler(realSubject); Subject proxySubject = (Subject) Proxy.newProxyInstance( realSubject.getClass().getClassLoader(), realSubject.getClass().getInterfaces(), handler ); // 调用代理对象 proxySubject.request(); } }
-
动态代理(CGLIB动态代理):
import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; // 真实对象 class RealSubject { public void request() { System.out.println("RealSubject handles request."); } } // CGLIB 动态代理处理器 class CglibProxyHandler implements MethodInterceptor { private Object realSubject; public CglibProxyHandler(Object realSubject) { this.realSubject = realSubject; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { // 额外的操作 System.out.println("CglibProxy handles request."); Object result = method.invoke(realSubject, objects); // 额外的操作 return result; } } // 客户端使用 CGLIB 动态代理 public class CglibProxyExample { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealSubject.class); enhancer.setCallback(new CglibProxyHandler(realSubject)); RealSubject cglibProxy = (RealSubject) enhancer.create(); // 调用代理对象 cglibProxy.request(); } }
-
虚拟代理:
虚拟代理是在需要的时候才创建和加载目标对象,延迟目标对象的实例化。// 接口 interface Image { void display(); } // 真实对象 class RealImage implements Image { private String filename; public RealImage(String filename) { this.filename = filename; loadImageFromDisk(); } private void loadImageFromDisk() { System.out.println("Loading image: " + filename); } @Override public void display() { System.out.println("Displaying image: " + filename); } } // 代理对象 class ProxyImage implements Image { private RealImage realImage; private String filename; public ProxyImage(String filename) { this.filename = filename; } @Override public void display() { if (realImage == null) { realImage = new RealImage(filename); } realImage.display(); } } // 客户端使用虚拟代理 public class VirtualProxyExample { public static void main(String[] args) { // 使用代理对象,实际的图片加载会在调用 display 方法时进行 Image proxyImage = new ProxyImage("example.jpg"); proxyImage.display(); } }
4. 实际开发场景
- 静态代理常用于简单场景,如权限控制、日志记录等。
- 动态代理在框架和库中广泛应用,如Spring AOP中的代理。
- 虚拟代理常用于需要延迟加载的场景,如大图加载。
5.总结
代理模式类型 | 定义 | 示例 |
---|---|---|
静态代理 | 在编译阶段确定代理关系,关系在代码中是固定的。 | 假设有 RealSubject (真实对象)和 Proxy (代理对象)两个类。Proxy 包含对 RealSubject 的引用,并在调用时添加额外的逻辑。 |
动态代理 | 在运行时通过反射等机制动态创建代理类,更加灵活。 | 1.JDK 动态代理示例:使用 InvocationHandler 和 Proxy 创建动态代理对象,不需要为每个被代理类编写专门的代理类。2.CGLIB 动态代理示例:通过 CGLIB 库创建代理对象,可以代理那些没有实现接口的类。 |
虚拟代理 | 在访问对象时才创建真实对象,适用于资源开销较大的情况。 | 假设有 Image 接口和具体类 RealImage (真实对象)和 ProxyImage (代理对象)。ProxyImage 在调用 display 方法时才创建并使用 RealImage 对象。 |