一、代理模式是什么?
代理模式是常见的设计模式之一。代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。(为真实对象提供代理,然后供其他对象通过代理访问真实对象)
二、结构
抽象主题(Subject):定义了真实主题和代理主题的共同接口,这样在任何使用真实主题的地方都可以使用代理主题。
真实主题(Real Subject):实现了抽象主题接口,是代理对象所代表的真实对象。客户端直接访问真实主题,但在某些情况下,可以通过代理主题来间接访问。
代理(Proxy):实现了抽象主题接口,并持有对真实主题的引用。代理主题通常在真实主题的基础上提供一些额外的功能,例如延迟加载、权限控制、日志记录等。
三、应用场景
演出:明星在一定阶段上只负责演唱,不负责一些日常事务,比如如何设置舞台,选地点,收钱等功能。这些可以通过经纪人进行处理,也就是通过代理模式实现了明星在这个演出功能上的增强。
四、实现
4.1静态代理
抽象主题
public interface IShow {
void show();
}
真实主题
public class Start implements IShow {
@Override
public void show() {
System.out.println("唱歌演出");
}
}
代理主题
public class IntermediaryProxy implements IRentHouse {
private IShow iShow ;
public IntermediaryProxy(IShow iShow) {
iShow =iShow ;
}
@Override
public void iShow () {
System.out.println("交中介费");
iShow .iShow ();
System.out.println("中介负责舞台等中间手续");
}
}
//client测试类
public class TestStaticProxy {
public static void main(String[] args) {
//定义明星
IShow iShow= new IShow ();
//定义中间经纪人
IShow intermediaryProxy = new IntermediaryProxy(iShow);
//中介演出
intermediaryProxy.show();
}
}
分析
从静态代理的代码中可以发现,静态代理的缺点显而易见,那就是当真实类的方法越来越多的时候,这样构建的代理类的代码量是非常大的,所以就引进动态代理.
4.2动态代理
什么是动态代理?
动态代理(Dynamic Proxy)是一种在运行时动态创建代理对象的技术。与静态代理相比,动态代理不需要显式地编写代理类,而是在程序运行过程中由 JVM 自动生成代理对象。
例子
【例】火车站卖票
如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了。这个例子其实就是典型的代理模式,火车站是目标对象,代售点是代理对象。
一、jdk动态代理
/**
* 卖票接口,定义卖票规则
*/
public interface SellTickets {
/**
* Sell.
*/
public void sell();
}
/**
* 火车站类,具有卖票功能,所以需要实现接口SellTickets
*/
public class TrainStation implements SellTickets {
public void sell() {
System.out.println("火车站卖票");
}
}
/**
* 代理工厂,用来创建代理对象
*/
public class ProxyFactory {
private TrainStation station = new TrainStation();
/**
* 代理工厂,动态获取代理对象
* @return
*/
public SellTickets getProxyObject(){
/**
* 调用Proxy代理类中的newProxyInstance方法来动态获取代理对象
* newProxyInstance()方法参数说明:
* ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可
* Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口
* InvocationHandler h : 代理对象的调用处理程序
*
*/
SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(
station.getClass().getClassLoader(), //类加载器,用来加载代理类
station.getClass().getInterfaces(),//真实对象实现的接口,代理对象跟真实对象实现同样的接口
new InvocationHandler() {//匿名内部类中定义代理对象要处理的业务逻辑
/**
*
* @param proxy 代理对象
* @param method 对应于在代理对象上调用的接口方法的 Method 实例
* @param args 代理对象调用接口方法时传递的实际参数
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理点收取一些服务费用(JDK动态代理方式)");
//执行真实对象 station
Object result = method.invoke(station, args);
return result;
}
});
return sellTickets;
}
}
/**
* 测试类
*/
public class Client {
public static void main(String[] args) {
//声明获取代理对象的工厂
ProxyFactory factory = new ProxyFactory();
//通过工厂获取代理对象
SellTickets proxyObject = factory.getProxyObject();
//代理对象执行业务逻辑
proxyObject.sell();
}
}
二、CGLIB动态代理
注意:
如果没有定义SellTickets接口,只定义了TrainStation(火车站类)。很显然JDK代理是无法使用了,因为JDK动态代理要求必须定义接口,对接口进行代理。
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。
导入依赖
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
package com.lxg.pattern.proxy.cglib_proxy;
/**
* 火车站类,没有实现任何接口
*/
public class TrainStation{
public void sell() {
System.out.println("火车站卖票");
}
}
/**
* CGLIB代理工厂
*/
public class ProxyFactory implements MethodInterceptor {
private TrainStation target = new TrainStation();
public TrainStation getProxyObject() {
//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数
Enhancer enhancer =new Enhancer();
//设置父类的字节码对象
enhancer.setSuperclass(target.getClass());
//设置回调函数
enhancer.setCallback(this);
//创建代理对象
TrainStation obj = (TrainStation) enhancer.create();
return obj;
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("代理点收取一些服务费用(CGLIB动态代理方式)");
TrainStation result = (TrainStation) methodProxy.invokeSuper(o, objects);
return result;
}
}
package com.lxg.pattern.proxy.cglib_proxy;
public class Client {
public static void main(String[] args) {
//创建代理工厂对象
ProxyFactory factory = new ProxyFactory();
//获取代理对象
TrainStation proxyObject = factory.getProxyObject();
proxyObject.sell();
}
}
什么时候使用JDK和CGLIB动态代理?
简单的来说,如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。
五、静态代理和动态代理分析
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题