代理模式是一种结构型设计模式,它允许你提供一个代理或替代品来控制对另一个对象的访问。代理对象通常充当客户端和实际目标对象之间的中介,可以用于各种用途,例如延迟加载、访问控制、监视、记录日志等。
一、代理模式包括以下几个关键角色
-
抽象主题(Subject): 定义了目标对象和代理对象的共同接口,客户端通过这个接口与目标对象交互。
-
具体主题(Real Subject): 实际的目标对象,代理对象所代表的真正业务逻辑。
-
代理(Proxy): 包含一个指向具体主题的引用,它实现了与抽象主题相同的接口,并可以在其方法中添加额外的逻辑。
二、静态代理
静态代理是代理模式的一种实现方式,它在编译期间就已经确定了代理关系。在静态代理中,代理类和目标类都必须在编码时已经存在,代理类通过实现与目标类相同的接口或继承与目标类相同的父类来实现代理。
静态代理的基本步骤和示例:
-
定义一个接口(或抽象类),用于声明目标对象和代理对象共同拥有的方法。
-
创建一个目标类,它实现了上述接口并提供了具体的业务逻辑。
-
创建一个代理类,它也实现了上述接口,并包含一个对目标对象的引用。代理类中的方法通常包含了一些额外的逻辑,例如日志记录、性能监测等。
-
在客户端代码中,创建目标对象和代理对象,然后通过代理对象调用方法。
下面是一个简单的 Java 示例,演示了静态代理的使用:
// 步骤1:定义接口
interface Subject {void doSomething();
}// 步骤2:创建目标类
class RealSubject implements Subject {public void doSomething() {System.out.println("RealSubject is doing something.");}
}// 步骤3:创建代理类
class Proxy implements Subject {private RealSubject realSubject;public Proxy() {this.realSubject = new RealSubject();}public void doSomething() {// 可以在这里添加额外的逻辑,如日志记录、性能监测等System.out.println("Proxy is doing something.");realSubject.doSomething();}
}// 步骤4:客户端代码
public class StaticProxyExample {public static void main(String[] args) {Subject proxy = new Proxy();proxy.doSomething();}
}
在上面的示例中,Subject
是接口,RealSubject
是目标类,Proxy
是代理类。代理类中的 doSomething
方法在调用目标对象的 doSomething
方法之前,添加了一些额外的逻辑。客户端代码通过代理对象调用方法。
静态代理的优点是简单,易于理解和实现。但它的缺点是每个代理类只能代理一个特定的目标类,如果需要代理多个不同的目标类,就需要创建多个代理类。此外,代理类的维护和扩展也可能会变得复杂。
总之,静态代理是一种简单而直观的代理模式实现方式,适用于特定的场景,但在某些情况下可能会引入大量的代理类。
三、Java 动态代理
java 动态代理是一种在运行时生成代理类的机制,用于代理其他类的方法调用。动态代理通常用于实现横切关注点(cross-cutting concerns)如日志、事务管理、性能监控等。Java 提供了两种主要的动态代理机制:基于接口的 JDK 动态代理和基于类的 CGLIB 动态代理。
1、JDK 动态代理
JDK 动态代理是 Java 标准库中的一部分,它要求被代理的类必须实现一个或多个接口。动态代理的核心类是 java.lang.reflect.Proxy
和 java.lang.reflect.InvocationHandler
。
下面是 JDK 动态代理的基本步骤:
-
创建一个实现
InvocationHandler
接口的代理处理器类,通常包含一个invoke
方法,该方法在代理对象的方法被调用时执行所需的逻辑。 -
使用
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)
方法创建代理对象。这个方法接受一个类加载器、一组接口和代理处理器。 -
使用代理对象调用方法。当调用代理对象的方法时,
invoke
方法将被调用,实际的逻辑由代理处理器定义。
示例代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;interface MyInterface {void doSomething();
}class MyRealObject implements MyInterface {public void doSomething() {System.out.println("Real object is doing something.");}
}class MyInvocationHandler implements InvocationHandler {private Object target;public MyInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method invocation.");Object result = method.invoke(target, args);System.out.println("After method invocation.");return result;}
}public class DynamicProxyExample {public static void main(String[] args) {MyInterface realObject = new MyRealObject();MyInvocationHandler handler = new MyInvocationHandler(realObject);MyInterface proxyObject = (MyInterface) Proxy.newProxyInstance(realObject.getClass().getClassLoader(),new Class<?>[] { MyInterface.class },handler);proxyObject.doSomething();}
}
2、CGLIB 动态代理
CGLIB(Code Generation Library)是一个强大的代码生成库,它可以在运行时动态生成类和方法。CGLIB 动态代理不要求被代理的类实现接口,它通过生成一个被代理类的子类来实现代理。
下面是 CGLIB 动态代理的基本步骤:
-
创建一个 CGLIB 代理对象,通常使用
Enhancer
类。 -
为代理对象设置目标类和方法拦截器(通常是一个方法拦截器类或回调函数)。
-
调用代理对象的方法,方法拦截器将在目标方法执行前后执行所需的逻辑。
示例代码:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;class MyRealClass {public void doSomething() {System.out.println("Real class is doing something.");}
}class MyMethodInterceptor implements MethodInterceptor {public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method invocation.");Object result = proxy.invokeSuper(obj, args);System.out.println("After method invocation.");return result;}
}public class CglibProxyExample {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(MyRealClass.class);enhancer.setCallback(new MyMethodInterceptor());MyRealClass proxyObject = (MyRealClass) enhancer.create();proxyObject.doSomething();}
}
注意:CGLIB 动态代理通常需要引入 CGLIB 库,而 JDK 动态代理是 Java 标准库的一部分。