SpringBoot使用设计模式一装饰器模式
一、前言
在实际开发中,经常会遇到这样的场景:某个核心业务功能已经实现,但随着需求迭代,需要为该功能添加额外的附加功能。例如,一个文件处理系统,核心功能是“文件上传”,后续可能需要新增“文件格式校验”“文件大小限制”“上传进度监控”“上传日志记录”等功能;再比如,一个接口调用工具,核心功能是“发送HTTP请求”,后续可能需要添加“请求参数加密”“响应结果解密”“超时重试”“调用耗时统计”等附加能力。
如果直接修改核心业务类的代码,会导致类的职责越来越臃肿,违反“单一职责原则”;如果通过继承的方式扩展,每增加一个附加功能就需要创建一个子类,会导致类的数量爆炸式增长,且灵活性极差(无法动态组合多个附加功能)。
针对这种“在不改变原有核心逻辑的前提下,动态为对象添加额外功能”的场景,我们可以使用装饰器模式来优雅解决。
二、装饰器模式
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许向一个现有的对象动态地添加新的功能,同时又不改变其结构。装饰器模式通过创建装饰器类,包裹原始对象,并在保持原始对象接口不变的前提下,提供额外的功能增强。
核心角色
- 抽象组件(Component):定义核心业务功能的公共接口,是被装饰者和装饰者的共同父类;
- 具体组件(Concrete Component):实现抽象组件接口,封装核心业务逻辑(即需要被装饰的原始对象);
- 抽象装饰器(Decorator):继承/实现抽象组件接口,持有抽象组件的引用,定义装饰器的公共行为(可包含通用的附加功能逻辑);
- 具体装饰器(Concrete Decorator):继承抽象装饰器,实现具体的附加功能,在调用原始组件方法的前后添加自定义逻辑。
使用场景
- 需在不修改原有代码的前提下,为对象动态添加/移除附加功能;
- 多个附加功能可灵活组合使用(继承无法实现组合功能);
- 避免因扩展功能导致的类爆炸(相比继承,装饰器模式更轻量化);
- 核心功能相对稳定,附加功能易变的场景。
优点
- 遵循“开闭原则”:扩展功能无需修改原有代码,只需新增装饰器类;
- 灵活性高:可动态组合多个装饰器,实现不同的功能组合;
- 职责单一:核心功能与附加功能分离,每个类仅负责单一职责;
- 避免类爆炸:相比继承,装饰器模式通过组合而非继承扩展功能,减少类的数量。
缺点
- 增加系统复杂度:引入多个装饰器类,可能导致代码结构层次增多;
- 调试难度提升:装饰器嵌套组合时,排查问题需要逐层追踪;
- 需维护组件接口一致性:装饰器必须与被装饰者实现相同的接口,否则无法透明替换。
三、实现案例
以“文件上传功能”为例,核心功能是“上传文件到服务器”,后续需要动态添加“文件格式校验”“文件大小限制”“上传日志记录”三个附加功能。下面通过装饰器模式实现该场景。
3.1 定义抽象组件(Component)
定义文件上传的核心接口,规范核心功能(文件上传)的行为:
/**
* 文件上传抽象组件(核心功能接口)
*/
public interface FileUploadService {
/**
* 核心功能:上传文件
* @param file 待上传文件(包含文件名、字节流、大小等信息)
* @return 上传结果(成功/失败、文件访问URL等)
*/
R<?> upload(FileInfo file);
}
定义文件信息实体类(封装上传文件的必要参数):
@Data
@NoArgsConstructor
@AllArgsConstructor
public class FileInfo implements Serializable {
private String fileName; // 文件名(含后缀)
private byte[] fileContent; // 文件字节流
private long fileSize; // 文件大小(单位:字节)
private String fileType; // 文件类型(如:image/jpeg、application/pdf)
}
3.2 实现具体组件(Concrete Component)
实现抽象组件接口,封装核心业务逻辑(单纯的文件上传功能,不包含任何附加功能):
/**
* 具体组件:基础文件上传实现(核心功能)
*/
@Service
@Slf4j
public class BaseFileUploadServiceImpl implements FileUploadService {
/**
* 核心逻辑:上传文件到服务器(模拟实际上传操作)
*/
@Override
public R<?> upload(FileInfo file) {
log.info("执行基础文件上传:文件名={},大小={}字节", file.getFileName(), file.getFileSize());
// 模拟上传逻辑:生成文件访问URL
String fileUrl = "https://file-server/" + System.currentTimeMillis() + "_" + file.getFileName();
return R.success("文件上传成功", fileUrl);
}
}
3.3 定义抽象装饰器(Decorator)
继承抽象组件接口,持有抽象组件的引用,为具体装饰器提供统一的父类,并可封装通用逻辑(如参数校验):
/**
* 抽象装饰器:文件上传装饰器父类
*/
public abstract class FileUploadDecorator implements FileUploadService {
// 持有抽象组件的引用(被装饰的对象)
protected FileUploadService fileUploadService;
// 构造方法注入被装饰的对象
public FileUploadDecorator(FileUploadService fileUploadService) {
this.fileUploadService = fileUploadService;
}
/**
* 实现核心接口方法:调用被装饰对象的upload方法,同时预留附加功能扩展点
* 子类可通过重写该方法,在调用前后添加附加功能
*/
@Override
public R<?> upload(FileInfo file) {
// 默认直接调用被装饰对象的核心方法(子类可覆盖重写)
return fileUploadService.upload(file);
}
}
3.4 实现具体装饰器(Concrete Decorator)
针对每个附加功能,创建对应的具体装饰器类,继承抽象装饰器,在核心功能执行前后添加附加逻辑。
3.4.1 文件格式校验装饰器
/**
* 具体装饰器:文件格式校验(附加功能)
*/
@Service
@Slf4j
public class FileTypeValidateDecorator extends FileUploadDecorator {
// 允许上传的文件类型(可配置在application.yml中,此处简化为硬编码)
private static final List<String> ALLOWED_FILE_TYPES = Arrays.asList(
"image/jpeg", "image/png", "application/pdf", "application/vnd.ms-excel"
);
// 构造方法注入被装饰的对象(通过Spring自动注入基础上传服务)
public FileTypeValidateDecorator(@Qualifier("baseFileUploadServiceImpl")
FileUploadService fileUploadService) {
super(fileUploadService);
}
/**
* 重写upload方法:添加文件格式校验逻辑
*/
@Override
public R<?> upload(FileInfo file) {
log.info("执行文件格式校验:文件类型={}", file.getFileType());
// 附加功能:校验文件类型是否允许上传
if (!ALLOWED_FILE_TYPES.contains(file.getFileType())) {
return R.fail("文件格式不支持,允许的格式:" + ALLOWED_FILE_TYPES);
}
// 调用被装饰对象的核心上传方法
return super.upload(file);
}
}
3.4.2 文件大小限制装饰器
/**
* 具体装饰器:文件大小限制(附加功能)
*/
@Service
@Slf4j
public class FileSizeLimitDecorator extends FileUploadDecorator {
// 最大允许上传大小(5MB,1MB=1024*1024字节,可配置在application.yml中)
private static final long MAX_FILE_SIZE = 5 * 1024 * 1024;
// 构造方法注入被装饰的对象
public FileSizeLimitDecorator(@Qualifier("baseFileUploadServiceImpl") FileUploadService fileUploadService) {
super(fileUploadService);
}
/**
* 重写upload方法:添加文件大小限制逻辑
*/
@Override
public R<?> upload(FileInfo file) {
log.info("执行文件大小校验:文件大小={}字节,最大限制={}字节", file.getFileSize(), MAX_FILE_SIZE);
// 附加功能:校验文件大小是否超出限制
if (file.getFileSize() > MAX_FILE_SIZE) {
return R.fail("文件大小超出限制,最大支持5MB");
}
// 调用被装饰对象的核心上传方法
return super.upload(file);
}
}
3.4.3 上传日志记录装饰器
/**
* 具体装饰器:上传日志记录(附加功能)
*/
@Service
@Slf4j
public class UploadLogDecorator extends FileUploadDecorator {
// 构造方法注入被装饰的对象
public UploadLogDecorator(@Qualifier("baseFileUploadServiceImpl") FileUploadService fileUploadService) {
super(fileUploadService);
}
/**
* 重写upload方法:添加日志记录逻辑(上传前后都记录)
*/
@Override
public R<?> upload(FileInfo file) {
// 上传前记录日志
log.info("开始上传文件:文件名={},类型={},大小={}字节",
file.getFileName(), file.getFileType(), file.getFileSize());
// 调用被装饰对象的核心上传方法(执行核心逻辑)
R<?> result = super.upload(file);
// 上传后记录日志(包含上传结果)
if (result.isSuccess()) {
log.info("文件上传成功:文件名={},访问URL={}", file.getFileName(), result.getData());
} else {
log.error("文件上传失败:文件名={},原因={}", file.getFileName(), result.getMessage());
}
return result;
}
}
3.5 控制器层调用(客户端)
客户端可通过组合不同的装饰器,动态为核心功能添加附加功能。装饰器支持嵌套组合(如:日志记录→格式校验→大小限制→基础上传):
/**
* 文件上传控制器(客户端调用入口)
*/
@RestController
@RequestMapping("/file")
public class FileUploadController {
// 注入基础上传服务(具体组件)
@Autowired
private FileUploadService baseFileUploadService;
// 注入各个装饰器
@Autowired
private FileTypeValidateDecorator fileTypeValidateDecorator;
@Autowired
private FileSizeLimitDecorator fileSizeLimitDecorator;
@Autowired
private UploadLogDecorator uploadLogDecorator;
/**
* 测试1:仅使用基础上传功能(无任何附加功能)
*/
@PostMapping("/upload/base")
public R<?> baseUpload(@RequestBody FileInfo file) {
return baseFileUploadService.upload(file);
}
/**
* 测试2:基础上传 + 格式校验 + 大小限制(组合两个装饰器)
*/
@PostMapping("/upload/validate")
public R<?> validateUpload(@RequestBody FileInfo file) {
// 组合装饰器:大小限制装饰器包裹格式校验装饰器,再包裹基础上传服务
FileUploadService decoratedService = new FileSizeLimitDecorator(
new FileTypeValidateDecorator(baseFileUploadService)
);
return decoratedService.upload(file);
}
/**
* 测试3:基础上传 + 格式校验 + 大小限制 + 日志记录(组合三个装饰器)
*/
@PostMapping("/upload/full")
public R<?> fullUpload(@RequestBody FileInfo file) {
// 嵌套组合:日志装饰器 → 大小限制 → 格式校验 → 基础上传
FileUploadService decoratedService = new UploadLogDecorator(
new FileSizeLimitDecorator(
new FileTypeValidateDecorator(baseFileUploadService)
)
);
return decoratedService.upload(file);
}
}
四、灵活扩展
4.1 动态组合装饰器(基于工厂模式优化)
上述客户端代码中,装饰器的组合是硬编码的,若需要频繁调整装饰器组合,可结合工厂模式封装装饰器的创建逻辑,提高灵活性:
/**
* 文件上传装饰器工厂(封装装饰器组合逻辑)
*/
@Component
public class FileUploadDecoratorFactory {
@Autowired
private FileUploadService baseFileUploadService;
/**
* 根据需求动态创建装饰器组合
* @param needLog 是否需要日志记录
* @param needTypeValidate 是否需要格式校验
* @param needSizeLimit 是否需要大小限制
* @return 组合后的文件上传服务
*/
public FileUploadService getDecoratedService(boolean needLog, boolean needTypeValidate,
boolean needSizeLimit) {
FileUploadService service = baseFileUploadService;
// 按需添加装饰器(顺序:从内到外,即先执行的装饰器后添加)
if (needTypeValidate) {
service = new FileTypeValidateDecorator(service);
}
if (needSizeLimit) {
service = new FileSizeLimitDecorator(service);
}
if (needLog) {
service = new UploadLogDecorator(service);
}
return service;
}
}
控制器层简化调用:
@RestController
@RequestMapping("/file")
public class FileUploadController {
@Autowired
private FileUploadDecoratorFactory decoratorFactory;
/**
* 动态组合装饰器(通过参数控制是否启用附加功能)
*/
@PostMapping("/upload/dynamic")
public R<?> dynamicUpload(@RequestBody FileInfo file,
@RequestParam(required = false, defaultValue = "true") boolean needLog,
@RequestParam(required = false, defaultValue = "true") boolean needTypeValidate,
@RequestParam(required = false, defaultValue = "true") boolean needSizeLimit) {
FileUploadService service = decoratorFactory.getDecoratedService(needLog,
needTypeValidate, needSizeLimit);
return service.upload(file);
}
}
4.2 新增附加功能(遵循开闭原则)
若后续需要新增“文件内容加密”功能,只需新增一个具体装饰器类,无需修改原有任何代码:
/**
* 新增具体装饰器:文件内容加密(附加功能)
*/
@Service
@Slf4j
public class FileEncryptDecorator extends FileUploadDecorator {
// 加密密钥(实际场景中应配置在安全存储中)
private static final String ENCRYPT_KEY = "file-encrypt-key-2024";
public FileEncryptDecorator(@Qualifier("baseFileUploadServiceImpl") FileUploadService fileUploadService) {
super(fileUploadService);
}
@Override
public R<?> upload(FileInfo file) {
log.info("执行文件内容加密:文件名={}", file.getFileName());
// 附加功能:对文件字节流进行加密(模拟AES加密)
byte[] encryptedContent = encrypt(file.getFileContent(), ENCRYPT_KEY);
file.setFileContent(encryptedContent);
// 调用后续装饰器或核心上传方法
R<?> result = super.upload(file);
// 可选:上传成功后记录加密日志
log.info("文件加密并上传完成:文件名={}", file.getFileName());
return result;
}
/**
* 模拟加密逻辑(实际场景使用标准加密算法如AES)
*/
private byte[] encrypt(byte[] content, String key) {
// 简化加密:将字节数组与密钥字节数组进行异或运算
byte[] keyBytes = key.getBytes(StandardCharsets.UTF_8);
byte[] encrypted = new byte[content.length];
for (int i = 0; i < content.length; i++) {
encrypted[i] = (byte) (content[i] ^ keyBytes[i % keyBytes.length]);
}
return encrypted;
}
}
新增装饰器后,通过工厂模式即可快速组合使用:
// 工厂类中新增组合逻辑
public FileUploadService getDecoratedService(boolean needLog, boolean needTypeValidate,
boolean needSizeLimit, boolean needEncrypt) {
FileUploadService service = baseFileUploadService;
if (needEncrypt) {
service = new FileEncryptDecorator(service);
}
if (needTypeValidate) {
service = new FileTypeValidateDecorator(service);
}
if (needSizeLimit) {
service = new FileSizeLimitDecorator(service);
}
if (needLog) {
service = new UploadLogDecorator(service);
}
return service;
}
五、与代理模式的区别
很多开发者会混淆装饰器模式和代理模式,两者都通过“包装对象”实现功能扩展,但核心目的和使用场景有明显区别:
| 对比维度 | 装饰器模式 | 代理模式 |
|---|---|---|
| 核心目的 | 动态为对象添加附加功能,强调功能扩展 | 控制对象的访问权限(如权限校验、延迟加载),强调访问控制 |
| 角色关系 | 装饰器与被装饰者实现相同接口,是 “平等” 的扩展关系 | 代理类与目标类无直接继承 / 实现关系(可通过接口关联),是 “上下级” 的控制关系 |
| 客户端感知 | 客户端明确知道自己使用了装饰器,可灵活组合 | 客户端通常不知道代理的存在,以为直接操作目标对象 |
| 功能增强时机 | 附加功能与核心功能是 “组合” 关系,可动态调整 | 增强逻辑是固定的(如代理类固定添加权限校验),不易动态调整 |
| 典型场景 | 文件上传附加校验/日志、接口调用附加重试/统计 | 权限控制、缓存代理、远程代理(RPC)、延迟加载 |
六、总结
本文通过“文件上传功能扩展”场景,详细展示了装饰器模式在SpringBoot中的落地实现,主要收获如下:
- 解决核心问题:在不修改原有核心代码的前提下,动态为对象添加附加功能,避免类臃肿和类爆炸;
- 遵循设计原则:严格遵循“开闭原则”(扩展功能只需新增装饰器)和“单一职责原则”(核心功能与附加功能分离);
- 灵活性极高:支持多个装饰器的嵌套组合,可根据需求动态调整功能组合方式;
- 低耦合:装饰器与被装饰者通过接口关联,依赖倒置,降低代码耦合度。
装饰器模式的适用场景非常广泛,除了文件上传,还可用于:
- 接口调用:添加超时重试、参数加密、响应缓存、耗时统计等;
- 数据处理:添加数据校验、格式转换、脱敏处理等;
- 日志记录:为不同业务操作动态添加日志打印(如操作日志、审计日志)。
在实际开发中,若遇到“核心功能稳定,附加功能易变且需灵活组合”的场景,装饰器模式是最优解决方案之一。结合工厂模式后,可进一步简化装饰器的组合与管理,提升代码的可维护性。

浙公网安备 33010602011771号