目录
一、代理模式
一句话概括
1.1、代理模式概述
1.2、静态代理
1.3、JDK 动态代理
1.4、CGLIB 动态代理
1.5、对比三种代理
1.5.1、jdk 代理 VS CGLIB 代理
1.5.2、动态代理 VS 静态代理
1.6、优缺点
1.7、使用场景
教你将类和对象结合再一起形成一个更强大的结构.
由于一些原因,我们不能直接访问目标对象,这时候需要提供一个目标对象的代理,通过代理来实现对目标对象的访问.
比如,我要去买火车票,就需要在火车触发的前几天坐车去火车站买票,相当的麻烦. 但是还有一种方式,就是我们可以直接在 12306 网站上直接进行买票,十分方便,这里的 12306 网站就相当于 代理.
代理模式分为以下三种角色:
静态代理表示在编译期间就创建好了代理对象.
这里还是使用刚刚提到的火车站买票的案例来实现.
/**
* 抽象主题:卖票接口
*/
public interface SellTickets {
void sell();
}
/**
* 真实主题类:火车站
*/
public class TrainStation implements SellTickets{
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
/**
* 代理类:代售点(例如 12306 网站)
*/
public class ProxyPoint {
//在编译时期对象就创建好了,因此是静态代理
private TrainStation trainStation = new TrainStation();
public void sell() {
System.out.println("方便了你,我自然要收取代理费用");
trainStation.sell();
}
}
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
//直接就可以通过代理类买到票,不用再去火车站了
ProxyPoint proxyPoint = new ProxyPoint();
proxyPoint.sell();
}
}
这里就可以看出测试类是通过 ProxyPoint 类访问到的目标方法,同时也对 sell 方法进行了增强(例如:收取代理费用).
Ps:JDK 动态代理要求必须要定义接口,因为他就是对接口进行代理的.
动态代理就是在程序运行期间,动态的在内存中创建出代理类(静态代理是在编译期间创建出代理类).
Java 中提供类一个动态代理类 Proxy(并不是 静态代理 中所说的代理对象类),提供了创建代理对象的静态方法(Proxy.newProxyInstance 方法)来获取代理对象.
这里继续使用买火车票案例
/**
* 抽象主题:卖票接口
*/
public interface SellTickets {
void sell();
}
/**
* 真实主题类:火车站
*/
public class TrainStation implements SellTickets {
@Override
public void sell() {
System.out.println("火车站卖票");
}
}
/**
* 代理类工厂(不是代理类):用来创建代理对象
*/
public class ProxyFactory {
private TrainStation station = new TrainStation();
public SellTickets getProxyObject() {
//使用 Proxy 获取代理对象
//newProxyInstance 参数如下:
//ClassLoader loader: 类加载器,此处填写真实对象的类加载器,是用来加载代理类的.
//Class>[] interfaces: 填写真实对象实现的接口
//InvocationHandler h: 代理对象要处理的业务逻辑
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(
station.getClass().getClassLoader(),
station.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//proxy: 代理对象,此方法中一般不用
//method: 对应于在代理对象上调用的接口方法的 Method 实例
//args: 代理对象调用接口方法时传递的实际参数
System.out.println("收取代理费用(JDK 动态代理)");
//执行真实对象
Object result = method.invoke(station, args);
return result;
}
}
);
return sellTickets;
}
}
public static void main(String[] args) {
ProxyFactory factory = new ProxyFactory();
//获取代理对象
SellTickets proxyObject = factory.getProxyObject();
proxyObject.sell();
}
执行流程如下:
同样是买火车票案例,如果没有定义 SellTickets 这个 抽象主题类,只定义了 TrainStation 火车列类,很显然 JDK 代理就无法使用了,因为 JDK 动态代理要求必须定义接口,对接口进行代理.
CGLIB 是一个功能强大,高性能的代码生成包. 他为没有实现接口的类提供了代理,给 JDK 动态代理提供了很好的补充.
CGLIB 是第三方提供的包,因此需要引入 jar 包.
cglib
cglib
2.2.2
具体实现如下:
/**
* 火车站
*/
public class TrainStation {
public void sell() {
System.out.println("火车站卖票");
}
}
public class ProxyFactory implements MethodInterceptor {
private TrainStation station = new TrainStation();
public TrainStation getProxyObject() {
//1.创建 Enhancer 对象,类似于 JDK 动态代理 Proxy 类
Enhancer enhancer = new Enhancer();
//2.设置父类(代理类的父类就是被代理类)的字节码对象
enhancer.setSuperclass(station.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建代理对象
TrainStation obj = (TrainStation) enhancer.create();
return obj;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
//这里的参数就和 JDK 动态代理的 invoke 很像了
//o: 代理对象
//method: 真实主题中的方法的 Method 实例
//objects: 实际参数
//methodProxy: 代理对象中的 method 实例
System.out.println("收取代理费用(CGLIB 动态代理)");
Object result = method.invoke(station, objects);
return result;
}
}
public class Client {
public static void main(String[] args) {
//创建代理类工厂
ProxyFactory factory = new ProxyFactory();
//获取代理对象
TrainStation proxyObject = factory.getProxyObject();
//调用代理对象中的 sell 方法
proxyObject.sell();
}
}
优点
中介,保护:代理模式在客户端和目标对象之间起到一个中介的作用和保护目标对象的作用.
扩展:代理对象可以扩展目标对象的功能.
解耦合:代理模式对客户端和目标对象进行解耦.
缺点:
实现起来相对复杂.