前言
作者目前在复习总结设计模式,便计划写一个设计模式的专题,本篇介绍的是代理模式,为了加深对代理模式认知,作者会记录实际项目中代理模式的实战。作者写博客主要是为了归纳总结技术,加深对它的理解,如有任何错误地方请帮忙指出。
业务需求
电商系统需要实现一个文件上传功能,考虑到成本和文件可能会较多前提下给出了以下方案:
- 大文件上传到阿里云SSO服务器上,如视频等;
- 小文件上传到本地FastDFS文件服务器上,如图片等。
功能设计
根据以上业务场景分析,得出的设计方案如下:
- 代理模式实现不同服务的文件上传 ;
- 策略模式降低程序中条件判断。
代理模式
模式定义
由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
模式结构
- 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
- 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
- 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
模式优缺点
优点:
- 代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
- 代理对象可以扩展目标对象的功能;
- 代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度,增加了程序的可扩展性。
缺点:
- 代理模式会造成系统设计中类的数量增加;
- 在客户端和目标对象之间增加一个代理对象,会造成请求处理速度变慢;
- 增加了系统的复杂度。
其他
设计模式是一种设计思想,不要将JDK动态代理、CGLIB及AOP等同于代理模式,这几种只是代理模式的实现方式而已。
设计模式实现
FileUpload抽象接口,定义了文件上传方法,分别给它写了2种实现
/**
* FileUpload抽象接口,定义了文件上传方法,分别给它写了2种实现
* @author winter
*/
public interface FileUpload {
/***
* 文件上传
* @param buffers:文件字节数组
* @param extName:后缀名
* @return
*/
String upload(byte[] buffers,String extName);
}
AliyunOSSFileUpload是将文件上传到aliyunOSS,主要上传mp4和avi的视频大文件
/**
* AliyunOSSFileUpload是将文件上传到aliyunOSS,主要上传mp4和avi的视频大文件
* @author winter
*/
@Component(value = "aliyunOSSFileUpload")
public class AliyunOSSFileUpload implements FileUpload {
@Value("${aliyun.oss.backurl}")
private String backurl;
@Override
public String upload(byte[] buffers, String extName) {
String realName = UUID.randomUUID().toString()+"."+extName ;
System.out.println("阿里云SSO上传。。。");
return backurl+realName;
}
}
FastdfsFileUpoad是将文件上传到FastDFS,主要上传png/jpg等图片小文件
/**
* FastdfsFileUpoad是将文件上传到FastDFS,主要上传png/jpg等图片小文件。
* @author winter
*/
@Component(value = "fastdfsFileUpoad")
public class FastdfsFileUpoad implements FileUpload {
@Value("${fastdfs.url}")
private String url;
@Override
public String upload(byte[] buffers, String extName) {
String realName = UUID.randomUUID().toString() + "." + extName;
System.out.println("fastdfs上传。。。");
return url + realName;
}
}
FileUploadProxy是代理对象,供用户访问,调用了FileUpload的文件上传方法,为用户提供不同文件上传调用。
@Data
@Component
@ConfigurationProperties(prefix = "upload")
public class FileUploadProxy implements ApplicationContextAware {
//继承ApplicationContextAware就可以拿到容器
private ApplicationContext act;
/**
* 读取配置文件中定义的配置
*/
private Map<String,List<String>> filemap;
/**
* 文件上传
* @param file 上传的文件
* @return
* @throws Exception
*/
public String upload(MultipartFile file) throws Exception {
//文件名字 1.mp4
String fileName = file.getOriginalFilename();
//扩展名 mp4,jpg
String extName = StringUtils.getFilenameExtension(fileName);
//循环filemap 策略模式降低程序中条件判断
for (Map.Entry<String, List<String>> entry : filemap.entrySet()) {
for (String suffix : entry.getValue()) {
//匹配当前extName和当前map中对应的类型是否匹配
if (extName.equalsIgnoreCase(suffix)) {
//一旦匹配,则把key作为唯一值,从容器中获取对应实例
return act.getBean(entry.getKey(),
FileUpload.class).upload(file.getBytes(), extName);
}
}
}
return null;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.act=applicationContext;
}
}
yml配置
filemap:
aliyunOSSFileUpload: mp4,avi
fastdfsFileUpload: png,jpg
#FastDFS配置 fastdfs:
url: http://localhost:28181/
#aliyun
aliyun:
oss:
backurl: https://sklll.oss-cn-beijing.aliyuncs.com/video/ #访问地址配置
spring:
servlet:
multipart:
max-file-size: 100MB #上传文件大小配置
FileController是控制器,用于接收用户提交的文件,并调用代理FileUploadProxy实现文件上传。
/**
* 文件上传
* @author winter
*/
@RestController
@RequestMapping(value = "/file")
public class FileController {
@Autowired
private FileUploadProxy fileUploadProxy;
/***
* 文件上传
* @param file
* @return
* @throws Exception
*
*/
@PostMapping(value = "/upload")
public String upload(MultipartFile file) throws Exception {
return fileUploadProxy.upload(file);
}
}
总结
代理模式的应用场景除了代码级别,还可以将代理模式迁移到应用以及架构级别,针对一 些图片小文件,我们可以直接把文件存储到FastDFS服务,针对大文件,例如商品视频介绍,我们可以把它存储到第 三方OSS。
用户通过文件上传代理服务可以间接访问OSS和本地FastDFS,这种分布式海量文件管理解决方案,这里不仅在代码 层面充分运用了代理模式,在架构层面也充分运用了代理模式。