代理模式
代理模式是一种结构型 设计模式,它提供了一种替代访问的方法,即通过代理对象来间接访问目标对象 。代理模式可以在不改变原始类代码的情况下,增加额外的功能,如权限控制、日志记录等。
静态代理
静态代理是指创建的或特定工具自动生成源代码,在程序运行前代理类的.class
文件就已经存在了。每个代理类只能为一个接口服务,如果需要代理多个接口,则需要编写多个代理类,这会增加维护成本。
示例:房子中介(静态代理)
java
// 房屋租赁服务接口
interface HouseLeaseService {
void leaseHouse();
}
// 真实主题类 - 房东
class Landlord implements HouseLeaseService {
public void leaseHouse() {
System.out.println("房东: 出租房屋.");
}
}
// 静态代理类 - 中介
class RealEstateAgent implements HouseLeaseService {
private Landlord landlord;
public RealEstateAgent(Landlord landlord) {
this.landlord = landlord;
}
@Override
public void leaseHouse() {
// 增加额外功能,例如广告宣传
System.out.println("中介: 发布租房信息.");
landlord.leaseHouse();
// 增加额外功能,例如收取中介费
System.out.println("中介: 收取中介费用.");
}
}
public class StaticProxyDemo {
public static void main(String[] args) {
Landlord landlord = new Landlord();
HouseLeaseService agentService = new RealEstateAgent(landlord);
agentService.leaseHouse();
}
}
在这个例子中,RealEstateAgent
作为代理类,不仅实现了HouseLeaseService
接口,还包含了对Landlord
对象的操作,并在调用前后添加了额外的行为。
--
动态代理
动态代理是在程序运行时动态生成代理类的字节码并加载到JVM中,因此不需要提前编写代理类的代码。Java提供了两种主要的动态代理方式:JDK动态代理和CGLIB。
JDK动态代理
在JDK动态代理中有两个重要的类Proxy
和InvocationHandler
类
-
Proxy
Proxy 提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
-
InvocationHandler 类
InvocationHandler 是代理实例的调用处理程序 实现的接口。
每个代理实例都具有一个关联的调用处理程序。对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的
invoke
方法。
代码示例
首先,我们定义一个实现了InvocationHandler
接口的处理器类ProxyInvocationHandler
,它负责封装中介的具体业务逻辑。
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 房屋租赁服务接口
interface HouseLeaseService {
void leaseHouse();
}
// 真实主题类 - 房东
class Landlord implements HouseLeaseService {
public void leaseHouse() {
System.out.println("房东: 出租房屋.");
}
}
// 实现InvocationHandler接口的处理器类 - 中介
class ProxyInvocationHandler implements InvocationHandler {
private final Object target;
public ProxyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用真实方法之前可以做一些额外的工作
System.out.println("中介: 发布租房信息.");
// 调用目标对象的方法
Object result = method.invoke(target, args);
// 在调用真实方法之后也可以做一些额外的工作
System.out.println("中介: 收取中介费用.");
return result;
}
}
public class JdkProxyDemo {
public static void main(String[] args) {
// 创建真实房东对象
Landlord landlord = new Landlord();
// 创建动态代理实例
HouseLeaseService proxyInstance = (HouseLeaseService) Proxy.newProxyInstance(
landlord.getClass().getClassLoader(),
landlord.getClass().getInterfaces(),
new ProxyInvocationHandler(landlord)
);
// 通过代理实例调用方法
proxyInstance.leaseHouse();
}
}
JDK动态代理与CGLIB动态代理的区别
特性 | JDK 动态代理 | CGLIB 动态代理 |
---|---|---|
实现方式 | 只能代理实现了接口的类(基于接口) | 通过生成目标类的子类进行代理(基于继承) |
代理类 | - Proxy - Invocation Handler | - Enhancer - Method Interceptor |
核心机制 | 使用反射调用目标方法 | 通过ASM字节码生成目标类的子类,重写方法实现代理 |
性能 (jdk8) | 在一百万运行次数内,性能快了30%左右;到五百万运行次数后,性能快了将近一倍 |