DesignPattern - 代理模式【结构型】
# 一、代理模式介绍
代理模式(Proxy Pattern)属于结构模式,为其他对象提供一种代理以控制对这个对象的访问,客户端并不直接调用实际的对象,而是通过调用代理,来间接的调用实际的对象。
核心作用:保护目标对象,增强目标对象(与装饰器模式类似)
核心组成
- Subject:抽象接口,目标对象类和代理类都要实现的一个抽象接口
- Proxy:代理类,包含了对目标对象类的引用,从而可以随意的操作目标对象的方法
- RealSubject:目标对象类,也叫委托类,或被代理类
应用场景
- 各大数据专营店,代理厂商进行销售对应的产品,代理商持有真正的授权代理书
- 客户端不想直接访问实际的对象,或者访问实际的对象存在困难,通过一个代理对象来完成间接的访问
- 想在访问一个类时做一些控制,或者增强功能
分类
- 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类,即代理类和委托类的关系在程序运行前就已经存在
- 动态代理:在运行期间使用动态生成字节码形式,动态创建代理类,常见的动态代理工具有 jdkproxy(基于反射原理,性能低)、cglib(基于 ASM 机制,生成被代理类的子类,高性能)
优点
- 可以在访问一个类时做一些控制,或增加功能
- 操作代理类无须修改原本的源代码,符合开闭原则,系统具有较好的灵活性和可扩展性
缺点
- 增加系统复杂性和调用链路
# 二、代理模式代码实现
以下以模拟文件上传为案例,分别使用 静态代理 与 动态代理 测算上传耗时。
创建抽象接口:
说明:该接口是必须的,代理类与被代理类都需要实现
/**
* Subject:抽象接口
*
* @author GitLqr
*/
public interface IUploader {
void upload(File file);
}
创建目标对象类,即被代理类:
/**
* RealSubject:目标对象类
*
* @author GitLqr
*/
public class HttpUploader implements IUploader {
@Override
public void upload(File file) {
try {
Thread.sleep(500);
System.out.println("使用 http 上传文件中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
# 1、静态代理
- 静态代理在代码编译时就确定了被代理的类
- 静态代理的代理类必须要实现和被代理类相同的接口
- 静态代理可以隐藏被代理类的具体实现;可以在不改变代理类的情况下增加额外的操作
创建静态代理类:
说明:静态代理类持有被代理对象引用,在对应的接口方法中,使用被代理对象来执行方法,并在方法执行前后记录时间戳,从而计算出方法耗时。
/**
* 代理类:静态代理
*
* @author GitLqr
*/
public class StaticProxy implements IUploader {
IUploader uploader;
public StaticProxy(IUploader uploader) {
super();
this.uploader = uploader;
}
@Override
public void upload(File file) {
long start = System.currentTimeMillis();
uploader.upload(file);
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");
}
}
使用:
说明:被代理对象可以直接在代理类中创建,从而做到更好的隐蔽性,同时扩展性也会降低,看实际需要决定
public static void main(String[] args) {
IUploader uploader = new HttpUploader();
IUploader uploaderProxy = new StaticProxy(uploader);
File file = new File("/");
uploaderProxy.upload(file);
}
# 2、动态代理
- 动态代理在代码运行时才动态创建代理类
- 动态代理相对于静态代理类,可以避免生成大量的代理类造成的冗余
动态代理可以通过 jdkproxy、cglib 等方式实现,以下以 jdkproxy(又称 JDK 动态代理) 为例
说明: jdkproxy 是 jdk 自带的,基于接口设计实现,只能代理接口中有的方法,且通过反射执行方法,性能低;cglib 需要依赖第三方库,但基于 ASM 机制,不依赖于接口,因为是 ASM 生成子类的方式(继承),性能也高一些
jdkproxy 涉及 2 个重要的类或接口:
java.lang.reflect.Proxy
:动态创建出来的代理对象的类,它提供了许多方法,用的最多的是Proxy#newProxyInstance()
,用于得到一个动态的代理对象java.lang.reflect.InvocationHandler
:是动态代理类要实现的接口,我们通过代理对象调用一个方法时,该方法会被转发给InvocationHandler
的invoke()
方法来调用
创建动态代理对象:
public static void main(String[] args) {
// 被代理对象
IUploader uploader = new HttpUploader();
ClassLoader classLoader = uploader.getClass().getClassLoader();
Class<?>[] interfaces = uploader.getClass().getInterfaces();
// 动态代理
IUploader uploaderProxy = (IUploader) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object result = method.invoke(uploader, args); // 注意:方法的执行者是被代理对象!!
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + "ms");
return result;
}
});
// 使用
File file = new File("/");
uploaderProxy.upload(file);
}
上次更新: 2024/11/24, 11:42:29
- 01
- Flutter - 子部件任意位置观察滚动数据11-24
- 02
- Flutter - 危!3.24版本苹果审核被拒!11-13
- 03
- Flutter - 轻松搞定炫酷视差(Parallax)效果09-21