代理模式(Proxy Pattern)是设计模式中的一种结构型模式,它为其他对象提供一种代理以控制对这个对象的访问。代理模式在不改变目标对象功能的前提下,为目标对象添加额外的处理逻辑或控制,比如权限验证、日志记录、事务处理等。通过引入代理对象,可以使得客户端代码与真实目标对象之间解耦,从而增强系统的灵活性和可扩展性。
抽象主题角色(Subject):定义代理类和真实主题的公共接口,这样可以在任何使用真实主题的地方都可以使用代理。
真实主题角色(Real Subject):定义了代理角色所代表的真实对象,是代理对象所代理的实体。
代理主题角色(Proxy Subject):代理角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;同时,代理角色可以在执行真实主题前后添加一些附加的操作,相当于对真实主题对象进行封装。
代理模式的分类
根据代理的创建时期,代理模式可以分为两种:
静态代理:代理类在程序运行前就已经存在,一般由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经被创建。
动态代理:动态代理类的源码是在程序运行时由JVM根据反射等机制动态生成的,所以不存在代理类的字节码文件。与静态代理类相比,动态代理更加灵活,但每次调用代理方法时,动态代理机制都会创建一个新的代理实例对象,所以速度相对较慢。
使用场景
远程代理(Remote Proxy):为一个位于不同的地址空间的对象提供一个本地的代理对象,这个不同的地址空间可以是本机的不同进程,也可以是网络上的一台远程服务器。
虚拟代理(Virtual Proxy):根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。
保护代理(Protect or Access Proxy):控制对原始对象的访问。保护代理用于控制不同用户对真实对象的访问权限。
智能指引(Smart Reference Proxy):提供了比简单调用更多的服务。例如,实现了自动缓存,当再次访问同一资源时,可以从缓存处直接返回,而不需要再次去创建或查询。
优缺点
优点:
代理模式能够协调调用者和被调用者,降低系统的耦合度。
在客户端和目标对象之间起到一个中介作用和保护目标对象的作用。
可以通过代理类添加额外的功能。
缺点:
代理模式会造成系统设计中类的数量增加。
在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢。
增加了系统的复杂度。
下面我将分别提供一个使用JDK动态代理和CGLIB代理的案例。
JDK动态代理案例
首先,我们需要定义一个接口和一个实现了该接口的类。然后,我们将创建一个动态代理类,该类将在运行时动态生成,并用于代理对真实对象的调用。
接口定义
public interface HelloService { String sayHello(String name);
}
真实类实现
public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "Hello, " + name; }
}
JDK动态代理实现
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; public class JdkProxyFactory { // 创建一个动态代理类 public static <T> T getProxyInstance(Class<T> interfaceClass, InvocationHandler handler) { return (T) Proxy.newProxyInstance( interfaceClass.getClassLoader(), // 类加载器 new Class<?>[]{interfaceClass}, // 需要代理的接口 handler // 调用处理器 ); } public static void main(String[] args) { HelloService realService = new HelloServiceImpl(); HelloService proxyService = JdkProxyFactory.getProxyInstance( HelloService.class, (proxy, method, args1) -> { System.out.println("Before method call"); Object result = method.invoke(realService, args1); System.out.println("After method call"); return result; } ); String result = proxyService.sayHello("World"); System.out.println(result); }
}
CGLIB代理案例
CGLIB代理是通过继承被代理类来创建代理对象的,因此它不需要接口。但是,由于它基于继承,所以无法代理final类或者final方法。
真实类定义(无需接口)
public class HelloService { public String sayHello(String name) { return "Hello, " + name; }
}
CGLIB代理实现
注意:CGLIB库不是JDK自带的,你需要通过Maven或Gradle等构建工具来引入它。
<!-- Maven依赖 -->
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version>
</dependency>
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxyFactory { public static <T> T getProxyInstance(Class<T> clazz, MethodInterceptor interceptor) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(interceptor); return (T) enhancer.create(); } public static void main(String[] args) { HelloService realService = new HelloService(); HelloService proxyService = CglibProxyFactory.getProxyInstance( HelloService.class, (obj, method, args, proxy) -> { System.out.println("Before method call"); Object result = method.invoke(realService, args); System.out.println("After method call"); return result; } ); String result = proxyService.sayHello("World"); System.out.println(result); }
}
在CGLIB的代理实现中,我们使用了Enhancer类来生成代理对象,并通过MethodInterceptor接口的实现来定义在方法调用前后的行为。
请注意,由于CGLIB代理是通过继承实现的,因此它不能代理final类或者final方法。此外,由于它需要在运行时生成新的类,因此可能会有一定的性能开销。而JDK动态代理则依赖于接口,因此不能代理没有实现接口的类。在选择使用哪种代理方式时,你需要根据你的具体需求来做出决定。
非常感谢你阅读到这里,如果这篇文章对你有帮助,希望能留下你的点赞👍 关注❤ 分享👥 留言💬thanks!!!