设计模式之代理模式
1. 概括
在编写代码的过程中,经常要对原有的代码进行修改,【增强】
但是如果直接修改源代码,长此以往代码应付变得,冗余、复杂、庞大
使用代理模式可以在不修改原有代码的基础上,并对原有代码的增强,并且增加了代码的隔离性
在调用时调用代理类的方法,由代理方法调用最终对象方法,在调用之前和之后,并且可以进行一定的增强
2. 结构
- 服务抽象接口:声明的服务方法,为客户端的最终调用方法
- 服务实体:实现服务接口,重写方法中的逻辑
- 代理类:同样实现服务接口,在重写的方法中调用最终执行的(服务实体)方法,在调用之前和之后,可以进行一定的增强
3. 使用场景
- 访问控制:不想直接访问某个对象中的方法
- 增强:对原有代码执行前后进行其它的操作,比如:打印日志、处理缓存等
4. 静态代理
- 示例:火车站卖票系统,卖票系统是接口,火车站是服务实体对象
- 使用代理对象访问,代理在 ”买票“ 之前,要收取服务费用
- 创建服务接口
public interface SellTickets {
void sell();
}
- 服务实体
public class TrainStation implements SellTickets{
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
- 代理类,同样实现卖票系统接口
public class ProxyPoint implements SellTickets{
// 被代理的对象,也就是最终服务
private SellTickets sellTickets;
public void setSellTickets(SellTickets sellTickets) {
this.sellTickets = sellTickets;
}
@Override
public void sell() {
// 增强
System.out.println("收取代理费用(静态代理)");
sellTickets.sell();
}
}
- 调用
public class Client {
public static void main(String[] args) {
// 创建代理对象
ProxyPoint proxy = new ProxyPoint();
// 设置最终调用的真实对象,
TrainStation trainStation = new TrainStation();
proxy.setSellTickets(trainStation);
// 调用代理对象中 增强后 的方法
proxy.sell();
}
}
5. 动态代理
如果在最终的调用对象中,不止一个方法,有多个方法需要我们代理增强,并且有可能还会是同样的增逻辑,比如,打印日志;那么静态代理显然会增加代理类的复杂度、冗余性
这里就可以使用动态代理
5.1 JDK动态代理
- JDK的动态代理基于接口实现,需要我们提供一个服务抽象接口【必需】
- 同样是上面的服务接口与实现类。代理类可以这么写
public class ProxyFactory {
private SellTickets sellTickets;
public void setSellTickets(SellTickets sellTickets) {
this.sellTickets = sellTickets;
}
// 返回参数类型的接口
public SellTickets getProxyObject() {
SellTickets proxyInstance = (SellTickets) Proxy.newProxyInstance(
sellTickets.getClass().getClassLoader(),
sellTickets.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("收取了代理费用(jdk动态代理)"); // 增强
Object obj = method.invoke(sellTickets, args);
return obj;
});
return proxyInstance;
}
}
-
注意:这里的
ProxyFactory
类不是代理类,而一个可以生成代理对象的工厂,getProxyObject()
返回的对象才是代理对象 -
返回的代理对象与被代理类【同级】的(实现同一接口),所以必需用接口接收
-
在
Proxy.newProxyInstance
方法中有三个参数ClassLoader loader
类加载器,用于加载被代理类Class<?>[] interfaces
真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口InvocationHandler h
代理对象的调用处理程序(回调函数),被代理类中的每个方法都会被这个接口实现类中的invoke
方法所调用,其中的参数proxy
代理对象,就是Proxy.newProxyInstance
方法的返回对象method
目标方法,被代理中的每个方法实例args
调用方法的参数列表
-
客户端
public class Client {
public static void main(String[] args) {
// 创建【最终】执行的对象
TrainStation trainStation = new TrainStation();
// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 工厂中的最终执行设置
proxyFactory.setSellTickets(trainStation);
// 获取代理对象,代理对象与最终执行对象是【同级】的
SellTickets proxy = proxyFactory.getProxyObject();
// 调用代理 【增强后】 的方法
proxy.sell();
}
}
5.2 CGLib动态代理
JDK动态代理必需依赖于接口,当我们要代理的类没有实现一个接口时,就可以用CGLib动态代理
CGLib是以继承被代理类的方式实现的,所以被代理类不可被final
修饰
依然是上面的例子
- 现在这个类没有实现任何的接口,我们要实现对它的代理
public class TrainStation {
public void sell() {
System.out.println("火车站卖票");
}
}
- 导入CGLib的依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
- 代理对象工厂
public class ProxyFactory {
private TrainStation trainStation;
public void setTrainStation(TrainStation trainStation) {
this.trainStation = trainStation;
}
public TrainStation getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(trainStation.getClass()); // 设置要代理谁
// 回调接口
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("收取一定的费用(增强)"); // 增强
Object obj = method.invoke(trainStation, objects);
return obj;
}
});
// 创建代理对象
TrainStation trainStation = (TrainStation) enhancer.create();
return trainStation;
}
}
- 回调中的参数
o
: 被代理对象method
: 真实对象中的方法的Method实例args
: 方法中的实际参数methodProxy
:代理对象中的方法的method实例
public class Client {
public static void main(String[] args) {
// 最终 调用对象
TrainStation trainStation = new TrainStation();
// 创建获取代理工厂对象
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTrainStation(trainStation);
// 获取【代理】对象
TrainStation proxy = proxyFactory.getProxy();
// 执行 代理中的 增强后的 方法
proxy.sell();
}
}
5.3 动态代理工厂通用抽取
- 可以用泛型使代理工厂通用化
- 其中的回调也可二次抽取
- JDK动态代理
public class ProxyGenericFactory<T> {
private T t;
public void setT(T t) {
this.t = t;
}
public T getProxy() {
T proxyInstance = (T) Proxy.newProxyInstance(
t.getClass().getClassLoader(),
t.getClass().getInterfaces(),
(proxy, method, args) -> {
System.out.println("收取了代理费用(jdk动态代理)"); // 增强
Object obj = method.invoke(t, args);
return obj;
}
);
return proxyInstance;
}
}
- CGLib
public class ProxyGenericFactory<T> {
private T t;
public void setT(T t) {
this.t = t;
}
public T getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(t.getClass());
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("增强"); // 增强
Object obj = method.invoke(t, objects);
return obj;
});
T proxy = (T) enhancer.create();
return proxy;
}
}