设计模式 代理模式(Proxy Pattern)
文章目录
- 简绍
- 静态代理
- 静态代理的特点
- 静态代理的应用场景
- 静态代理的缺点
- jdk动态代理
- JDK 动态代理的工作原理
- JDK 动态代理的关键组件
- invoke 方法签名
- Proxy.newProxyInstance 方法被用来创建一个动态代理对象。以下是该方法调用的详细解释:
- 获取类加载器
- 指定接口
- 提供 InvocationHandler
- cglib代理。
- CGLIB 代理的工作原理
- CGLIB 的基本使用步骤
- intercept 方法的参数解释
- 使用 MethodProxy 调用目标方法
- 使用 invokeSuper 方法:
- 使用 invoke 方法:
简绍
代理模式是一种结构型设计模式,它允许您提供一个替代对象(代理)来控制对一个真实对象的访问。这种模式通常用于在访问某个对象之前或之后执行一些额外的操作,比如缓存、日志记录、权限验证等
静态代理
静态代理的特点
- 代理类和真实主题类在编译时确定:
代理类和真实主题类都是在编写代码时就确定的,并且它们通常共享一个公共接口。 - 代理类包含对真实主题类的引用:
代理类包含一个对真实主题类实例的引用,并通过该引用调用真实主题的方法。 - 代理类可以执行额外的操作:
代理类可以在调用真实主题的方法之前或之后执行一些额外的操作,如日志记录、权限验证等。
静态代理的应用场景
- 日志记录:
在调用方法前后记录日志。 - 权限验证:
在调用方法前验证用户权限。 - 缓存:
缓存方法的结果,以避免重复计算。 - 事务管理:
在调用方法前后管理事务。 - 性能优化:
延迟加载或异步处理。
静态代理的缺点
静态代理的一个缺点是,对于每一个真实主题类,都需要创建一个代理类。在需要为多个类创建代理的情况下,这可能会导致大量的代理类。
创建基础调用类
public interface Mobile {
void buy(String mobileName);
}
原有的实现方式
public class XiaomiProxy implements Mobile{
@Override
public void buy(String mobileName) {
System.out.println("购买到手一台: "+ mobileName);
}
}
通过对原有基础类调用 ,封装之前的调用逻辑,在上面附加一层处理方式
public class PayProxy implements Mobile{
private XiaomiProxy xiaomiProxy;
public PayProxy(){
xiaomiProxy = new XiaomiProxy();
}
@Override
public void buy(String mobileName) {
System.out.println("准备刷卡购买: " + mobileName);
xiaomiProxy.buy(mobileName);
}
}
两种 结果, 我们可以在代理类中去进行各种数据处理
public class Main {
public static void main(String[] args) {
XiaomiProxy xiaomiProxy = new XiaomiProxy();
xiaomiProxy.buy("小米4");
System.out.println("-----------------------");
PayProxy payProxy = new PayProxy();
payProxy.buy("小米4");
}
}
jdk动态代理
JDK 动态代理是一种在运行时生成代理类的技术,它允许您为任何实现了接口的类创建代理对象。与静态代理不同,动态代理不需要显式地编写代理类的代码,而是通过 Java 反射 API 自动生成代理类。这种方式更加灵活,因为代理类是在运行时动态生成的。
JDK 动态代理的工作原理
- 创建 InvocationHandler 实现类:
创建一个实现了 InvocationHandler 接口的类,该类将定义代理行为。 - 创建代理对象:
使用 java.lang.reflect.Proxy 类的 newProxyInstance 方法创建代理对象。 - 调用代理对象的方法:
通过代理对象调用方法,实际执行的是 InvocationHandler 中定义的方法。
JDK 动态代理的关键组件
- Interface (接口):
代理对象和真实主题对象必须实现相同的接口。 - RealSubject (真实主题):
这是实际执行请求的对象。 - InvocationHandler:
这是一个接口,它的实现类定义了代理行为。 - Proxy (代理):
由 java.lang.reflect.Proxy 类创建的代理对象。
invoke 方法签名
invoke 方法是 Java 反射机制中的一个核心部分,特别是在使用动态代理时。它定义在 java.lang.reflect.InvocationHandler 接口中,并且在代理对象的方法被调用时由 Java 虚拟机 (JVM) 自动触发
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
-
这是调用该 invoke 方法的代理实例对象。
当客户端通过代理对象调用一个方法时,这个方法实际上会委托给 InvocationHandler 中的 invoke 方法。该参数可以用于获取代理对象的信息,例如使用 proxy.getClass().getName()。有时可以将代理对象返回以进行连续调用,即链式调用,因为 this 并不是代理对象本身。 -
method
:
这是在代理实例上调用的接口方法对应的 Method 实例。
它包含了关于要调用的方法的信息,如方法名、参数类型、返回类型等。
可以使用这个参数来决定如何处理方法调用,例如是否需要执行某些预处理或后处理逻辑。 -
args
:
这是一个 Object 数组,包含了在代理对象上调用方法时传递的参数。
参数按照方法定义的顺序排列。
如果方法没有参数,则此数组为空。
public class PayProxyHandle implements InvocationHandler {
private final Object target;
public PayProxyHandle(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(target, args);
}
}
Proxy.newProxyInstance 方法被用来创建一个动态代理对象。以下是该方法调用的详细解释:
获取类加载器
Mobile.class.getClassLoader()
这一行获取了 Image 接口的类加载器。类加载器是负责加载类到 JVM 的对象。在大多数情况下,您可以使用类的 getClassLoader() 方法来获取类加载器,但是基本类型和 Object 类没有类加载器,它们是由特殊的 Bootstrap ClassLoader 加载的。对于普通的类和接口,通常可以使用 .class 字段来获取其类加载器。
指定接口
ew Class<?>[]{Mobile.class}
这一行创建了一个接口数组,这里只有一个接口 Image。Proxy.newProxyInstance 方法需要知道代理对象需要实现哪些接口。
提供 InvocationHandler
new PayProxyHandle(xiaomiProxy)
这一行创建了一个 PayProxyHandle 类的实例,它实现了 InvocationHandler 接口。InvocationHandler 接口定义了一个 invoke 方法,该方法会在代理对象的方法被调用时被调用。DynamicProxyHandler 类需要处理代理对象上发生的所有方法调用,并根据需要执行额外的逻辑。
public class Main {
public static void main(String[] args) {
Mobile o =(Mobile) Proxy.newProxyInstance(
Mobile.class.getClassLoader(),
new Class<?>[]{Mobile.class},
new PayProxyHandle(xiaomiProxy));
o.buy("小米4");
}
}
JDK 动态代理是一种强大的工具,它允许您在不修改现有类的情况下为现有类添加新的行为。通过使用动态代理,您可以轻松地扩展系统的功能,同时保持代码的整洁和模块化。动态代理的一个主要优势是无需为每个真实主题类创建一个代理类,因此可以减少代码量并提高灵活性。
cglib代理。
CGLIB(Code Generation Library)是一种强大的、高性能且动态的字节码生成库。它可以在运行时创建一个指定类的新子类。这种能力使得 CGLIB 成为 AOP(面向切面编程)框架和其他需要在运行时动态创建子类的应用的理想选择。CGLIB 不依赖于接口,因此它可以用于代理没有实现接口的类。
CGLIB 代理的工作原理
- 代理类生成:
CGLIB 使用字节码技术在运行时动态生成代理类。
生成的代理类是目标类的一个子类,并且覆盖了目标类的所有非最终方法。
这意味着所有非最终方法都可以被拦截并执行额外的逻辑。 - 方法拦截:
CGLIB 提供了一个 MethodInterceptor 接口,该接口定义了一个 intercept 方法。
每当代理类的方法被调用时,intercept 方法就会被触发。
在 intercept 方法内,你可以执行预处理和后处理逻辑,以及调用原始方法。 - 代理对象创建:
代理对象通过 CGLIB 的 Enhancer 类创建。你提供一个 Callback 或者实现 MethodInterceptor 的对象,该对象定义了如何处理方法调用。
CGLIB 的基本使用步骤
- 导入 CGLIB 相关依赖:
- 如果你使用 Maven 或 Gradle,需要添加 CGLIB 的依赖到你的项目中。
- 定义目标类:
定义一个目标类,该类不需要实现任何接口。 - 创建 MethodInterceptor:
- 实现 MethodInterceptor 接口,定义 intercept 方法。
- 在 intercept 方法中,你可以执行一些预处理操作,然后调用原始方法,最后执行后处理操作。
- 创建代理对象:
- 使用 CGLIB 的 Enhancer 类来创建代理对象。
- 设置目标类、回调函数等信息。
- 使用代理对象:
- 使用生成的代理对象代替原始对象。
CGLIB 的优缺点
- 优点:
支持对所有方法的拦截,即使这些方法没有声明为 public。
不需要目标类实现特定接口,适用于没有实现接口的类。
高性能,尤其是在多次调用相同方法的情况下。 - 缺点:
对于最终类 (final) 和最终方法 (final) 无法创建代理。
由于使用了字节码生成技术,可能会产生额外的 CPU 和内存开销。
安装 CGLIB
<dependency>
<groupId>net.sf.cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
intercept 方法的参数解释
intercept 方法的签名如下:
Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
- obj: 代理对象本身。这是 CGLIB 动态生成的代理类的实例。
- method: java.lang.reflect.Method 类型的对象,表示正在被调用的方法。
- args: 方法调用时传递的实际参数数组。
- proxy: MethodProxy 类型的对象,用于调用目标方法。
使用 MethodProxy 调用目标方法
MethodProxy 提供了两种主要的方式来调用目标方法:
使用 invokeSuper 方法:
Object result = proxy.invokeSuper(obj, args);
这种方式会直接调用代理对象的父类(即目标类)的方法。
使用 invoke 方法:
Object result = proxy.invoke(obj, args);
这种方式也会调用目标方法,但它会经过 CGLIB 的代理机制。
通常情况下,建议使用 invokeSuper 方法,因为它更高效并且避免了潜在的无限递归问题。
public class TargetMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
}
在 CGLIB 中,Enhancer 类是用来创建动态代理的工具类。
// 创建 Enhancer 实例
Enhancer enhancer = new Enhancer();
// 设置父类
enhancer.setSuperclass(TargetClass.class);
// 设置 Callback
enhancer.setCallback(new TargetMethodInterceptor());
// 创建代理对象
TargetClass proxy = (TargetClass) enhancer.create();
// 使用代理对象
proxy.test("test");
CGLIB 是一种强大的工具,可以用于创建动态代理,尤其是在不支持 JDK 动态代理的场景下(即目标类没有实现接口)。它通过字节码生成技术在运行时创建代理类,并且能够拦截所有的非最终方法。CGLIB 代理广泛应用于 AOP 框架中,例如 Spring AOP。