SpringBoot策略模式+工厂模式实战解析

一、引言

在 Java 开发中,我们经常遇到这样的场景:根据不同的类型或条件,执行不同的业务逻辑。传统的写法往往是使用大量的 `if-else` 或 `switch-case`,随着业务增长,这种代码会变得难以维护。

今天,我要分享一套在实际项目中使用的高性能策略模式+工厂模式组合,能够让你的代码优雅百倍。

二、核心类设计

2.1 基础策略接口

复制代码
/**
 * <p>
 * 基础策略接口 - 定义策略模式的标准实现
 * </p>
 * <p>
 * 实现此接口的类可通过 {@link BaseStrategyFactory} 进行注册和管理,
 * 支持按类型获取对应的策略实现。
 * </p>
 *
 * @param <K> 策略标识类型(如枚举),用于唯一标识每个策略
 * @param <Q> 请求参数类型,策略执行时需要的输入参数
 * @param <R> 返回结果类型,策略执行后的输出结果
 * @author Just.YangJie
 * @version 1.0.1
 * @since 2026-06-02
 */
public interface BaseStrategy<K, Q, R> extends InitializingBean {

    /**
     * 获取策略的唯一标识键
     *
     * @return K 策略标识,用于在工厂中注册和查找策略
     */
    K getKey();

    /**
     * 执行策略逻辑
     *
     * @param request Q 策略执行所需的请求参数
     * @return R 策略执行后的结果
     */
    R execute(Q request);
}

2.2 基础工厂类

复制代码
/**
 * <p>
 * 基础策略工厂 - 提供策略的注册与获取功能
 * </p>
 * <p>
 * 使用全局注册表管理所有策略类型,支持根据类型和标识键获取对应的策略实现。
 * 采用线程安全的 {@link ConcurrentHashMap} 存储,确保并发环境下的稳定性。
 * </p>
 *
 * @param <K> 策略标识类型(如枚举)
 * @param <S> 策略实现类型,必须实现 {@link BaseStrategy} 接口
 * @author Just.YangJie
 * @version 1.0.1
 * @since 2026-06-02
 */
public abstract class BaseStrategyFactory<K, S extends BaseStrategy<K, ?, ?>> {

    private static final Map<Class<?>, Map<Object, BaseStrategy<?, ?, ?>>> GLOBAL_REGISTRY = new ConcurrentHashMap<>();

    /**
     * 获取指定策略类型的注册表
     */
    @SuppressWarnings("unchecked")
    protected static <K, S extends BaseStrategy<K, ?, ?>> Map<K, S> registry(Class<S> type) {
        return (Map<K, S>) GLOBAL_REGISTRY.computeIfAbsent(type, k -> new ConcurrentHashMap<>());
    }

    /**
     * 注册策略实例到全局注册表
     * <p>
     * 如果相同类型和键已存在,则不会覆盖已有策略。
     * </p>
     */
    public static <K, S extends BaseStrategy<K, ?, ?>> void register(Class<S> type, K key, S strategy) {
        Map<K, S> map = registry(type);
        map.putIfAbsent(key, strategy);
    }

    /**
     * 根据类型和标识键获取策略实例
     */
    public static <K, S extends BaseStrategy<K, ?, ?>> S get(Class<S> type, K key) {
        Map<K, S> map = registry(type);
        return map.get(key);
    }

    /**
     * 根据类型和标识键获取策略实例,若不存在则返回默认值
     */
    public static <K, S extends BaseStrategy<K, ?, ?>> S getOrDefault(Class<S> type, K key, S defaultStrategy) {
        Map<K, S> map = registry(type);
        return map.getOrDefault(key, defaultStrategy);
    }
}

三、设计亮点

3.1 三大核心优势

特性 说明
自动注册 实现 `InitializingBean`,在 Bean 初始化时自动注册到工厂
线程安全 使用 `ConcurrentHashMap` 作为全局注册表
类型安全 通过泛型确保策略与标识的类型一致

3.2 类图结构

四、实战案例:文件读取策略

4.1 业务场景

我们需要一个文件读取模块,支持 PDF、Word 等多种格式的内容提取。

4.2 定义标识枚举

复制代码
/**
 * 文件后缀 - 枚举
 */
@Getter
public enum FileExtensionEnum {

    PDF("pdf", FileType.DOCUMENT),
    DOC("doc", FileType.DOCUMENT),
    DOCX("docx", FileType.DOCUMENT),
    // ... 其他格式

    ;

    private final String extension;
    private final FileType fileType;

    FileExtensionEnum(String extension, FileType fileType) {
        this.extension = extension;
        this.fileType = fileType;
    }
}

4.3 定义请求响应对象

复制代码
// 请求对象
@Data
@Builder
public class FileReadRequest {
    private String path;           // 文件路径
    private FileExtensionEnum extension;  // 文件扩展名
}

// 响应对象
@Data
@Builder
public class FileReadResponse {
    private String content;       // 文件内容
}

4.4 扩展策略接口

复制代码
/**
 * 文档读取策略接口
 */
public interface FileReaderStrategy
    extends BaseStrategy<FileExtensionEnum, FileReadRequest, FileReadResponse> {

    /**
     * 策略注册方法 - 自动注册到工厂
     */
    default void afterPropertiesSet(FileExtensionEnum key) {
        FileReaderStrategyFactory.register(key, this);
    }

    /**
     * 读取文件内容 (自定义方法名)
     */
    FileReadResponse reader(FileReadRequest request);

    @Override
    default FileReadResponse execute(FileReadRequest request) {
        return reader(request);
    }
}

4.5 具体策略实现

PDF 读取策略:

复制代码
@Slf4j
@Component
public class PdfReaderStrategy implements FileReaderStrategy {

    @Override
    public FileExtensionEnum getKey() {
        return FileExtensionEnum.PDF;
    }

    @Override
    public void afterPropertiesSet() {
        // 自动注册到工厂
        afterPropertiesSet(getKey());
    }

    @Override
    public FileReadResponse reader(FileReadRequest request) {
        try (PDDocument document = PDDocument.load(new File(request.getPath()))) {
            PDFTextStripper stripper = new PDFTextStripper();
            String content = stripper.getText(document);
            return FileReadResponse.builder().content(content).build();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new CustomException(e.getMessage());
        }
    }
}

**Word 读取策略:**

复制代码
@Slf4j
@Component
public class WordReaderStrategy implements FileReaderStrategy {

    @Override
    public FileExtensionEnum getKey() {
        return FileExtensionEnum.DOCX;
    }

    @Override
    public void afterPropertiesSet() {
        // Word 支持 .doc 和 .docx 两种格式
        afterPropertiesSet(getKey());
        afterPropertiesSet(FileExtensionEnum.DOC);
    }

    @Override
    public FileReadResponse reader(FileReadRequest request) {
        // 根据不同格式处理
        if (FileExtensionEnum.DOC == request.getExtension()) {
            return readDocFormat(request);
        }
        return readDocxFormat(request);
    }

    private FileReadResponse readDocxFormat(FileReadRequest request) {
        StringBuilder builder = new StringBuilder();
        try (FileInputStream inputStream = new FileInputStream(request.getPath());
             XWPFDocument document = new XWPFDocument(inputStream)) {
            // 读取段落
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                builder.append(paragraph.getText());
            }
            // 读取表格
            for (XWPFTable table : document.getTables()) {
                table.getRows().forEach(row ->
                    row.getTableCells().forEach(cell -> builder.append(cell.getText())));
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return FileReadResponse.builder().content(builder.toString()).build();
    }
}

4.6 业务工厂类

复制代码
/**
 * 文档内容读取 工厂
 */
public class FileReaderStrategyFactory extends BaseStrategyFactory<FileExtensionEnum, FileReaderStrategy> {

    /**
     * 获取策略实例
     */
    public static FileReaderStrategy getStrategy(FileExtensionEnum key) {
        return BaseStrategyFactory.get(FileReaderStrategy.class, key);
    }

    /**
     * 注册策略
     */
    public static void register(FileExtensionEnum key, FileReaderStrategy strategy) {
        BaseStrategyFactory.register(FileReaderStrategy.class, key, strategy);
    }
}

4.7 调用示例

复制代码
@Service
public class FileService {

    /**
     * 读取文件内容
     */
    public String readFile(String path, FileExtensionEnum extension) {
        // 1. 根据文件类型获取对应策略
        FileReaderStrategy strategy = FileReaderStrategyFactory.getStrategy(extension);

        // 2. 构建请求
        FileReadRequest request = FileReadRequest.builder()
                .path(path)
                .extension(extension)
                .build();

        // 3. 执行策略获取结果
        FileReadResponse response = strategy.execute(request);

        return response.getContent();
    }
}

五、注册原理详解

5.1 InitializingBean 自动注册机制

Spring 容器在 Bean 初始化完成后,会调用 `afterPropertiesSet()` 方法。我们利用这个钩子实现了**自动注册**:

复制代码
```
Bean 创建 → afterPropertiesSet() 被调用 → 策略注册到工厂
```

5.2 全局注册表结构

复制代码
GLOBAL_REGISTRY: Map<Class<?>, Map<Object, BaseStrategy<?, ?, ?>>>

第一层 Key: 策略实现类的 Class 对象 (如 FileReaderStrategy.class)
第一层 Value: 该类型下所有策略的 Map
第二层 Key: 策略的唯一标识 (如 FileExtensionEnum.PDF)
第二层 Value: 策略实例对象

**访问流程:**

  1. 调用 `FileReaderStrategyFactory.getStrategy(FileExtensionEnum.PDF)`

  2. 定位到 `FileReaderStrategy.class` 注册表

  3. 根据 `PDF` 键找到 `PdfReaderStrategy` 实例

六、更多应用场景

场景 策略标识 具体策略
支付 PayTypeEnum AlipayStrategy, WechatPayStrategy, UnionPayStrategy
压缩 CompressTypeEnum ZipStrategy, RarStrategy, TarStrategy
导出 ExportTypeEnum PdfExportStrategy, ExcelExportStrategy, WordExportStrategy
短信 SmsChannelEnum AliSmsStrategy, TencentSmsStrategy, HuaweiSmsStrategy
存储 StorageTypeEnum OssStorageStrategy, S3StorageStrategy, LocalStorageStrategy

七、总结

通过策略模式+工厂模式的组合,我们实现了:

  1. **代码解耦**:每个策略独立实现,互不依赖

  2. **扩展性强**:新增策略只需实现接口,无需修改现有代码

  3. **性能优异**:全局注册表 + ConcurrentHashMap,O(1) 查找

  4. **自动注册**:利用 Spring 生命周期,零配置实现策略注册

  5. **类型安全**:泛型确保编译期检查,避免运行时错误

相关推荐
wuminyu3 小时前
Java锁机制之轻量级锁判断与尝试逻辑源码剖析
java·linux·c语言·jvm·c++
☆cwlulu4 小时前
Linux系统调用与C库I/O的底层奥秘
java·spring boot·spring
柏舟飞流4 小时前
Spring Boot 深入实践指南:从入门到工程化落地
spring boot·后端·firefox
橘子海全栈攻城狮4 小时前
【最新源码】鸟博士微信小程序 023
spring boot·后端·web安全·微信小程序·小程序
阳光满路5 小时前
三步搞定:Linux 安装配置 Telnet 服务
linux·运维·centos
码农编程录5 小时前
【notes9】
linux
河阿里5 小时前
Spring Boot:整合Quartz集群部署指南
java·spring boot·后端
砍材农夫6 小时前
物联网实战:Spring Boot MQTT | 模拟器Paho客户端拆解高性能
java·javascript·spring boot·后端·物联网·struts