赞
踩
代理模式是常见的设计模式之一,代理模式就是代理对象具备真实对象的功能,能代替真实对象完成相应操作,并能在操作执行的前后进行增强处理。
代理模式常应用于面向切面编程(AOP)、日志记录、权限控制、性能监控等多种横切关注点的系统级服务。
代理模式的实现可以分为两类:一类是静态代理,另一类是动态代理。
静态代理在程序运行前就已经存在,指由程序员需要手动编写或使用特定工具生成代理类的源代码(即.class 文件),在程序编译阶段就已确定。
静态代理需要为每个目标类手动编写代理类,可能导致代码重复。适用于代码结构相对固定且代理类数量有限的场景。
动态代理是在程序运行期间动态生成的。代理不是直接写成 .class 文件,而是在JVM运行时通过反射、字节码操作(如Java的 java.lang.reflect.Proxy 类或第三方库如CGLIB)等机制动态构建的。
动态代理提供了更高的灵活性和扩展性,但会牺牲少许性能。
先理解静态代理的实现,更容易理解JDK动态代理的原理,其使用流程如下:
// 接口 interface Subject { // 需要被增强的方法 void request(); } // 实现类 class RealSubject implements Subject { @Override public void request() { System.out.println("RealSubject: Handling request."); } } // 静态代理类 class StaticProxy implements Subject { // 成员变量用于指向被代理的对象,使用接口类型申明该变量 private final Subject realSubject; // 可以用构造器或者set方法来设置被代理的对象 public StaticProxy(Subject realSubject) { this.realSubject = realSubject; } @Override public void request() { // 可选的前置处理 preRequest(); // 调用被代理的对象 realSubject.request(); // 可选的后置处理 postRequest(); } private void preRequest() { System.out.println("StaticProxy: Pre-processing request."); } private void postRequest() { System.out.println("StaticProxy: Post-processing request."); } } // 使用静态代理对象 public class StaticProxyTest { public static void main(String[] args) { // 使用接口申明变量类型,指向静态代理类的实例对象 Subject subject = new StaticProxy(new RealSubject()); subject.request(); } }
JDK动态代理是一种利用Java反射机制在运行时动态创建代理对象的技术,它是Java原生支持的,主要依赖于java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。
JDK 动态代理基于接口,只有实现了接口的类,才能通过 JDK 动态代理来增强接口方法。
JDK动态代理的使用流程如下:
InvocationHandler
接口,并且需要定义接口类型的成员变量,用于指向被代理的对象。实现invoke()
方法,可以对接口方法进行增强。Proxy.newProxyInstance()
返回的代理对象。// 动态代理处理器 class DynamicProxyHandler implements InvocationHandler { private final Subject realSubject; public DynamicProxyHandler(Subject realSubject) { this.realSubject = realSubject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { preRequest(); // 调用被代理对象的方法 // 调用接口方法时,这里就会调用被代理对象的相应方法 Object result = method.invoke(realSubject, args); postRequest(); return result; } private void preRequest() { System.out.println("DynamicProxyHandler: Pre-processing request."); } private void postRequest() { System.out.println("DynamicProxyHandler: Post-processing request."); } } // 测试动态代理 public class DynamicProxyTest { public static void main(String[] args) { Subject realSubject = new RealSubject(); // 返回的是Object对象,需要转换为目标接口对象 Subject proxySubject = (Subject) Proxy.newProxyInstance( RealSubject.class.getClassLoader(), new Class[]{Subject.class}, new DynamicProxyHandler(realSubject) ); proxySubject.request(); } }
CGLIB(Code Generation Library)是一个强大的、高性能的代码生成库,它允许在运行时动态地生成和修改Java字节码。
CGLIB动态代理基于子类继承,能够对任何类(无论是否实现了接口)实现代理,这使得它的应用范围更广。
CGLIB动态代理的使用流程如下:
MethodInterceptor
接口。实现intercept()
方法来对被代理对象的方法进行增强。Enhancer
对象,通过Enhancer
对象来返回代理对象。该操作可以封装在方法拦截器中。// 目标类,无需实现接口 class RealSubject { public void request() { System.out.println("RealSubject: Handling request."); } } // CGLIB代理实现 class CglibProxy implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { preRequest(); Object result = proxy.invokeSuper(obj, args); // 调用父类方法,即真实对象的方法 postRequest(); return result; } private void preRequest() { System.out.println("CglibProxy: Pre-processing request."); } private void postRequest() { System.out.println("CglibProxy: Post-processing request."); } // 创建代理对象 public Object getProxy(Class<?> clazz) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); // 设置父类 enhancer.setCallback(this); // 设置回调方法 return enhancer.create(); // 创建并返回代理对象 } } // 测试CGLIB动态代理 public class CglibProxyTest { public static void main(String[] args) { CglibProxy cglibProxy = new CglibProxy(); // 返回的是Object对象,需要转换为被代理类的对象 RealSubject proxySubject = (RealSubject) cglibProxy.getProxy(RealSubject.class); proxySubject.request(); } }
实现代理逻辑
都需要调用目标方法,然后在目标方法前后新增增强逻辑。区别是,静态代理在实现接口方法时,调用被代理对象的目标方法并实现增强。动态代理需要实现接口(InvocationHandler / MethodInterceptor),必须实现接口方法(invoke / intercept),在接口方法里调用被代理对象的目标方法并实现增强。
获取代理对象
静态代理时,被代理类的对象即代理对象。
JDK动态代理中,使用Proxy.newProxyInstance()
来获取代理对象。
CGLIB动态代理中,使用enhancer.create()
来获取代理对象。
静态代理和CGLIB动态代理都依赖于接口,CGLIB动态代理不依赖于接口。
静态代理中,增强逻辑分散在被代理类的各个方法中,需要在方法中实现该方法的增强逻辑。如果这些方法都需要同种增强,将产生大量的重复代码。
在动态代理中,增强逻辑集中在一个方法里。如果所有方法都需要同种增强,增强逻辑只需要写一处,非常方便。如果方法的增强逻辑不同,则需要通过反射获取方法名,然后使用响应的增强逻辑。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。