代理模式
代理模式
代理模式是一种结构型设计模式,其主要目的是控制对对象的访问。在代理模式中,代理对象充当了被代理对象的中间人,客户端通过代理对象来间接访问被代理对象,从而可以在访问过程中添加额外的功能或控制访问的权限。其作用包括:
- 对客户端透明:客户端无需知道代理对象的存在,可以直接通过代理对象来访问被代理对象,从而降低了客户端与被代理对象之间的耦合度。
- 控制访问:代理对象可以在访问过程中添加额外的功能,如权限验证、日志记录、性能监控等。
- 简化代码逻辑:代理对象可以隐藏一些复杂的操作,使得客户端代码更加简洁清晰。
静态代理
在编译时就确定代理类和被代理类的关系,代理类直接持有被代理类的引用,并且代理类和被代理类都实现了相同的接口或继承自相同的父类,相当于是多写了一个代理类,在调用的时候调用的是代理类,在代理类中的处理还是原生的处理逻辑,不过在前后添加上需要添加的代码。 这样做的缺点就是每个被代理类都需要一个接口和对应的代理类。
实现方式(被代理类已经写好了自己的逻辑):
- 定义接口,其中定义被代理类需要被代理的方法,代理类和被代理类同时实现该接口。
- 代理类实现接口对应的方法,在其中实现额外的逻辑并且调用被代理的原生方法。
- 需要使用代理方法时,直接使用代理类中的方法
动态代理
在运行时动态地创建代理对象,代理对象通过实现一组接口或者继承一个父类,动态地处理被代理对象的方法调用,并且可以在方法调用前后添加额外的逻辑。
JDK动态代理
静态代理中需要手动实现代理类之后进行编译,并且每个被代理类都需要不同的代理类,这是非常麻烦的,Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例; 动态代理是通过 Proxy 创建代理对象,然后将接口方法“代理”给 InvocationHandler 完成的。但是,该方法只能代理实现了接口的类。
Proxy
该类提供了一个静态方法 newProxyInstance
,用于创建代理对象。该方法接受三个参数:ClassLoader、一组接口(被代理类实现的接口)和一个 InvocationHandler 对象,然后在运行时动态生成代理类并返回代理对象。
1 | public static Object newProxyInstance(ClassLoader loader, |
InvocationHandler
该接口定义了一个方法 invoke
,用于处理被代理对象的方法调用。在实现该接口时,需要编写处理方法调用的逻辑。
1 | //方法的返回值就是代理对象 |
实现方式:
- 需要被代理类实现接口,该接口中定义被代理类需要被代理的方法,这点和静态代理相似
- 实现InvocationHandler和invoke方法(自定义类或使用匿名内部类),并在invoke方法中编写代理逻辑和调用逻辑
- 使用Proxy.newProxyInstance方法创建代理对象,通过调用该代理对象的对应方法即可完成被代理类的方法代理(该代理对象与原代理对象实现了相同接口,是一种多态,是原对象的增强版,因此通过使用该代理对象的相应方法可以完成对被代理类的增强)。
CGLib动态代理
CGLib(Code Generation Library)是一个基于字节码生成库,它允许在运行时动态地生成代理类。与 JDK 动态代理不同,CGLib 动态代理可以代理没有实现接口的类。因此CGLib 动态代理相比于 JDK 动态代理更加灵活,它可以代理没有实现接口的类,并且不需要编写额外的接口。然而,CGLib 动态代理性能相对较差,因为它是通过生成子类来实现代理的,而不是像 JDK 动态代理那样直接使用接口。CGLib 动态代理通常涉及以下两个重要的类:
- Enhancer 类:是 CGLib 的核心类之一,用于生成代理类的实例。Enhancer 类提供了一系列方法,用于配置和生成代理类。
- MethodInterceptor 接口:类似于 JDK 的 InvocationHandler 接口,MethodInterceptor 定义了一个方法
intercept
,用于处理被代理对象的方法调用。
使用示例
1 | import net.sf.cglib.proxy.Enhancer; |
实现原理
CGLib 的实现原理主要基于字节码生成和类加载技术。
- 字节码生成:
- CGLib 使用 ASM(一个基于 Java 字节码操作的框架)来生成字节码。
- 在代理类中,CGLib会动态生成一个新的类,该类继承自被代理类,因此被代理类不需要实现任何接口。
- CGLib会在代理类中重写被代理类的方法,并在方法内部调用 MethodInterceptor 中定义的逻辑。
- 类加载:
- CGLib 通过创建字节码生成器来动态生成代理类的字节码,并将其转换为 Class 对象。
- CGLib 使用默认的类加载器来加载生成的代理类。
- 代理对象创建:
- 在代理对象创建过程中,CGLib 会生成一个新的代理类实例。
- 当客户端调用代理对象的方法时,CGLib 会调用代理类中重写的方法,并在其中执行 MethodInterceptor 中定义的逻辑。
- MethodInterceptor 接口:
- MethodInterceptor 接口定义了一个方法
intercept
,用于处理被代理对象的方法调用。 - 实现 MethodInterceptor 接口的类可以通过
intercept
方法来实现对被代理对象方法的拦截和处理。
- MethodInterceptor 接口定义了一个方法
Spring AOP与动态代理
- 在 Spring AOP 中,使用了动态代理来实现切面对被代理对象的方法调用的拦截和处理。
- 当目标对象实现了接口时,Spring AOP 使用 JDK 动态代理来创建代理对象。
- 当目标对象没有实现接口时,Spring AOP 使用 CGLib 动态代理来创建代理对象。
- Spring AOP 将切面的通知逻辑织入到目标对象的方法调用中,从而实现对横切关注点的统一管理和处理。