目 录CONTENT

文章目录

Spring Boot 优雅处理多态 JSON 入参与策略模式结合实践

在等晚風吹
2024-12-03 / 0 评论 / 0 点赞 / 9 阅读 / 0 字 / 正在检测是否收录...

Spring Boot 优雅处理多态 JSON 入参与策略模式结合实践

在 Spring Boot 开发中,如何优雅地处理多态 JSON 入参并结合策略模式,根据 type 动态调用对应的逻辑,是一个非常实用的技术点。本文将详细介绍如何实现这一功能,并通过结合策略模式动态选择组件,使代码结构更加清晰、扩展性更强。


一、场景描述

假设我们有一个通用的 OCR 接口,接收不同类型的识别请求。请求体包含一个 type 字段,用于指明需要调用的具体识别逻辑,例如:

  • ID 卡识别ID_CARD
  • 车辆识别VEHICLE
  • 营业执照识别BUSINESS_LICENSE
  • 车票识别CAR_INVOICE

请求体示例如下:

{
    "type": "ID_CARD",
    "idNumber": "123456789",
    "name": "John Doe"
}

我们希望在控制器接收到请求后,能够根据 type 字段的值动态调用对应的识别组件,同时保持代码的高内聚和低耦合。


二、解决方案概述

实现上述功能的核心包括以下几个步骤:

  1. 设计多态 JSON 入参结构:使用 @JsonTypeInfo@JsonSubTypes 注解。
  2. 利用策略模式,根据 type 字段选择对应的逻辑组件。
  3. 通过 Spring 注入管理策略组件,实现松耦合的动态调用。
  4. 完善异常处理机制,提供用户友好的错误提示。

三、详细实现

3.1 定义通用父类和子类

首先,我们设计一个通用的父类 CommonOCRDTO,并通过 Jackson 的多态注解支持根据 type 字段解析为不同的子类。

父类设计
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME, 
    include = JsonTypeInfo.As.PROPERTY, 
    property = "type"
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = IdCardRecognizerDTO.class, name = "ID_CARD"),
    @JsonSubTypes.Type(value = VehicleOCRDTO.class, name = "VEHICLE"),
    @JsonSubTypes.Type(value = BusinessLicenseOCRDTO.class, name = "BUSINESS_LICENSE"),
    @JsonSubTypes.Type(value = CarInvoiceOCRDTO.class, name = "CAR_INVOICE")
})
public abstract class CommonOCRDTO {
    private String type;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
}
子类设计

每个子类对应具体的 OCR 请求参数。例如:

public class IdCardRecognizerDTO extends CommonOCRDTO {
    private String idNumber;
    private String name;

    // Getters and Setters
}

public class VehicleOCRDTO extends CommonOCRDTO {
    private String vehicleNumber;
    private String owner;

    // Getters and Setters
}

3.2 定义 OCR 策略接口和实现类

策略接口
public interface OCRStrategy<T extends CommonOCRDTO, R> {
    String getType(); // 返回策略对应的 type 值

    R process(T request); // 处理请求
}
策略实现类

根据不同的 OCR 类型,实现对应的逻辑。例如:

@Service
public class IdCardOCRStrategy implements OCRStrategy<IdCardRecognizerDTO, String> {

    @Override
    public String getType() {
        return "ID_CARD";
    }

    @Override
    public String process(IdCardRecognizerDTO request) {
        return "Processing ID Card for: " + request.getName();
    }
}

@Service
public class VehicleOCRStrategy implements OCRStrategy<VehicleOCRDTO, String> {

    @Override
    public String getType() {
        return "VEHICLE";
    }

    @Override
    public String process(VehicleOCRDTO request) {
        return "Processing Vehicle for: " + request.getVehicleNumber();
    }
}

3.3 实现策略选择器

利用 Spring 的 @Component 和依赖注入机制管理所有策略类,通过 type 动态选择策略。

@Component
public class OCRStrategyContext {

    private final Map<String, OCRStrategy<?, ?>> strategyMap = new HashMap<>();

    @Autowired
    public OCRStrategyContext(List<OCRStrategy<?, ?>> strategies) {
        for (OCRStrategy<?, ?> strategy : strategies) {
            strategyMap.put(strategy.getType(), strategy);
        }
    }

    @SuppressWarnings("unchecked")
    public <T extends CommonOCRDTO, R> OCRStrategy<T, R> getStrategy(String type) {
        OCRStrategy<?, ?> strategy = strategyMap.get(type);
        if (strategy == null) {
            throw new IllegalArgumentException("No strategy found for type: " + type);
        }
        return (OCRStrategy<T, R>) strategy;
    }
}

3.4 Controller 中调用策略

在 Controller 中,根据 type 字段动态选择对应的策略并调用处理逻辑:

@RestController
@RequestMapping("/ocr")
public class OCRController {

    private final OCRStrategyContext strategyContext;

    @Autowired
    public OCRController(OCRStrategyContext strategyContext) {
        this.strategyContext = strategyContext;
    }

    @PostMapping("/recognize")
    public ResponseEntity<Object> recognize(@RequestBody CommonOCRDTO request) {
        OCRStrategy<CommonOCRDTO, Object> strategy = strategyContext.getStrategy(request.getType());
        Object result = strategy.process(request);
        return ResponseEntity.ok(result);
    }
}

3.5 全局异常处理

为了提升用户体验,我们需要对无效的 type 值或其他异常提供友好的提示。

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(IllegalArgumentException.class)
    public ResponseEntity<String> handleIllegalArgumentException(IllegalArgumentException ex) {
        return ResponseEntity.badRequest().body(ex.getMessage());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        return ResponseEntity.badRequest().body("Invalid JSON format or unknown type.");
    }
}

四、完整流程示例

4.1 请求示例

  • 请求体:
{
    "type": "ID_CARD",
    "idNumber": "123456789",
    "name": "John Doe"
}
  • 响应示例:
{
    "result": "Processing ID Card for: John Doe"
}
  • 如果传入无效的 type
{
    "type": "UNKNOWN_TYPE"
}
  • 响应示例:
{
    "error": "No strategy found for type: UNKNOWN_TYPE"
}

五、总结

通过结合 Jackson 多态反序列化策略模式,我们实现了以下目标:

  1. 简化了多态 JSON 入参的解析:

    • 使用 @JsonTypeInfo@JsonSubTypes 动态解析入参。
  2. 动态选择业务逻辑组件:

    • 使用策略模式封装每种 OCR 类型的处理逻辑。
    • 利用 Spring 的依赖注入自动管理和选择策略。
  3. 增强了代码的可扩展性:

    • 新增业务类型时,只需添加新的策略实现类和子类。
  4. 完善了异常处理:

    • 针对无效的 type 或 JSON 格式错误提供友好的错误提示。

通过这种设计,我们实现了优雅的多态 JSON 入参处理和高效的动态业务逻辑调用,为类似场景提供了一种通用的解决方案。

0

评论区