自定义注解实现服务分处理-策略模式

路由:请求标识→匹配 Service→调用 process 方法

通过自定义注解 @BusinessServiceMapping 标记具体业务 Service,注解值(如 DC 代表客户、ORD 代表订单)与请求参数中的业务标识关联;再通过 Spring 容器扫描 + 策略模式,实现 “请求标识→匹配 Service→调用 process 方法” 的自动路由,避免硬编码分支判断。

根据条件动态选择执行,解耦 “选择逻辑” 和 “业务实现”

自定义注解:@BusinessServiceMapping(标记业务 Service)

import org.springframework.stereotype.Component;
import java.lang.annotation.*;

/**
 * 自定义业务Service映射注解:标记Service对应的业务标识(如DC=客户、ORD=订单)
 */
@Target({ElementType.TYPE}) // 仅用于类(Service实现类)
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,便于反射获取
@Documented
@Component // 让Spring自动扫描该注解标记的类
public @interface BusinessServiceMapping {

    /**
     * 业务标识(如DC=客户、ORD=订单),支持多标识(如一个Service处理多个相关业务)
     */
    String[] value();

    /**
     * 业务描述(可选,用于文档说明)
     */
    String desc() default "";
}

2. 业务 Service 接口:BusinessProcessService(定义统一方法)

import com.yourproject.dto.BusinessRequestDTO;
import com.yourproject.dto.BusinessResponseDTO;

/**
 * 业务Service统一接口:所有业务Service需实现此接口
 */
public interface BusinessProcessService {

    /**
     * 统一业务处理方法
     * @param requestDTO 业务请求参数(封装请求标识、业务数据等)
     * @return 业务处理结果
     */
    BusinessResponseDTO process(BusinessRequestDTO requestDTO);

    /**
     * 获取当前Service支持的业务标识(与@BusinessServiceMapping的value对应,便于校验)
     */
    String[] getSupportBusinessCodes();
}

3. 业务请求 / 响应 DTO:封装参数与结果

// 业务请求DTO:请求需携带业务标识(如dc、ord)
public class BusinessRequestDTO {
    /**
     * 业务标识(与@BusinessServiceMapping的value对应,如DC=客户、ORD=订单)
     */
    private String businessCode;

    /**
     * 具体业务数据(JSON字符串,根据业务类型解析)
     */
    private String businessData;

    // getter + setter
}

// 业务响应DTO:统一返回格式
public class BusinessResponseDTO {
    /**
     * 处理状态(SUCCESS/FAIL)
     */
    private String status;

    /**
     * 响应消息
     */
    private String message;

    /**
     * 业务结果数据(JSON字符串)
     */
    private String resultData;

    // 静态工厂方法:简化创建
    public static BusinessResponseDTO success(String message, String resultData) {
        BusinessResponseDTO dto = new BusinessResponseDTO();
        dto.setStatus("SUCCESS");
        dto.setMessage(message);
        dto.setResultData(resultData);
        return dto;
    }

    public static BusinessResponseDTO fail(String message) {
        BusinessResponseDTO dto = new BusinessResponseDTO();
        dto.setStatus("FAIL");
        dto.setMessage(message);
        return dto;
    }

    // getter + setter
}

4. 业务 Service 实现类(示例:客户 Service + 订单 Service)

// 示例1:客户业务Service(标识DC)
@BusinessServiceMapping(
        value = "DC", // 业务标识:DC=客户
        desc = "客户相关业务处理(新增/编辑/查询客户)"
)
public class CustomerBusinessService implements BusinessProcessService {

    @Override
    public BusinessResponseDTO process(BusinessRequestDTO requestDTO) {
        // 1. 解析客户业务数据(requestDTO.getBusinessData()为JSON字符串)
        String customerData = requestDTO.getBusinessData();
        // 示例:假设业务数据是客户新增参数(实际项目用JSON工具解析为实体类)
        // CustomerAddDTO addDTO = JSON.parseObject(customerData, CustomerAddDTO.class);

        // 2. 处理客户业务逻辑(如调用DAO新增客户)
        System.out.println("客户Service处理业务,数据:" + customerData);

        // 3. 返回结果
        return BusinessResponseDTO.success("客户业务处理成功", "{\"customerId\":1001,\"status\":\"ACTIVE\"}");
    }

    @Override
    public String[] getSupportBusinessCodes() {
        // 与@BusinessServiceMapping的value保持一致(便于校验)
        return new String[]{"DC"};
    }
}

// 示例2:订单业务Service(标识ORD)
@BusinessServiceMapping(
        value = "ORD", // 业务标识:ORD=订单
        desc = "订单相关业务处理(创建订单/取消订单)"
)
public class OrderBusinessService implements BusinessProcessService {

    @Override
    public BusinessResponseDTO process(BusinessRequestDTO requestDTO) {
        // 订单业务逻辑处理(类似客户Service)
        String orderData = requestDTO.getBusinessData();
        System.out.println("订单Service处理业务,数据:" + orderData);

        return BusinessResponseDTO.success("订单业务处理成功", "{\"orderId\":2001,\"orderStatus\":\"PAID\"}");
    }

    @Override
    public String[] getSupportBusinessCodes() {
        return new String[]{"ORD"};
    }
}

5. 路由服务:BusinessServiceRouter(核心:匹配请求与 Service)

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;

/**
 * 业务Service路由:根据请求的businessCode匹配对应的Service
 */
@Component
public class BusinessServiceRouter implements ApplicationContextAware {

    /**
     * 业务标识→Service的映射缓存(key:businessCode,value:对应的BusinessProcessService)
     */
    private final Map<String, BusinessProcessService> businessServiceMap = new HashMap<>();

    /**
     * Spring启动时,扫描所有带@BusinessServiceMapping的Service,初始化映射
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        // 1. 扫描Spring容器中所有实现BusinessProcessService接口的Bean
        Map<String, BusinessProcessService> serviceBeans = 
                applicationContext.getBeansOfType(BusinessProcessService.class);

        // 2. 遍历Service,解析@BusinessServiceMapping注解,建立映射
        for (BusinessProcessService service : serviceBeans.values()) {
            BusinessServiceMapping mapping = service.getClass().getAnnotation(BusinessServiceMapping.class);
            if (mapping == null) {
                continue; // 跳过未标记注解的Service
            }

            // 3. 为每个业务标识绑定Service(支持一个Service对应多个标识)
            for (String businessCode : mapping.value()) {
                if (businessServiceMap.containsKey(businessCode)) {
                    throw new RuntimeException("业务标识[" + businessCode + "]重复绑定Service,请检查@BusinessServiceMapping注解");
                }
                businessServiceMap.put(businessCode, service);
                System.out.println("业务标识[" + businessCode + "]绑定Service:" + service.getClass().getSimpleName());
            }
        }

        // 4. 打印映射结果(便于调试)
        System.out.println("业务Service映射初始化完成,共" + businessServiceMap.size() + "个标识:" + businessServiceMap.keySet());
    }

    /**
     * 核心方法:根据业务标识获取对应的Service
     * @param businessCode 业务标识(如DC、ORD)
     * @return 匹配的BusinessProcessService
     * @throws RuntimeException 无匹配Service时抛出异常
     */
    public BusinessProcessService getServiceByBusinessCode(String businessCode) {
        BusinessProcessService service = businessServiceMap.get(businessCode);
        if (service == null) {
            throw new RuntimeException("未找到业务标识[" + businessCode + "]对应的Service,请检查配置");
        }
        return service;
    }

    /**
     * 统一业务处理入口:路由到对应Service并调用process方法
     */
    public BusinessResponseDTO routeAndProcess(BusinessRequestDTO requestDTO) {
        if (requestDTO == null || requestDTO.getBusinessCode() == null) {
            return BusinessResponseDTO.fail("请求参数错误:businessCode不能为空");
        }

        try {
            // 1. 根据businessCode获取Service
            BusinessProcessService service = getServiceByBusinessCode(requestDTO.getBusinessCode());

            // 2. 调用Service的process方法处理业务
            return service.process(requestDTO);
        } catch (RuntimeException e) {
            // 3. 捕获异常,返回统一错误格式
            return BusinessResponseDTO.fail("业务处理失败:" + e.getMessage());
        }
    }
}

6. 控制层:BusinessController(接收请求,调用路由服务)

import com.yourproject.dto.BusinessRequestDTO;
import com.yourproject.dto.BusinessResponseDTO;
import com.yourproject.service.BusinessServiceRouter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 统一业务入口Controller:所有业务请求通过此接口进入,由路由服务分发
 */
@RestController
@RequestMapping("/api/business")
public class BusinessController {

    @Autowired
    private BusinessServiceRouter businessServiceRouter;

    /**
     * 统一业务处理接口
     * @param requestDTO 业务请求(包含businessCode和业务数据)
     * @return 业务处理结果
     */
    @PostMapping("/process")
    public BusinessResponseDTO processBusiness(@RequestBody BusinessRequestDTO requestDTO) {
        // 直接委托路由服务处理,无需硬编码分支
        return businessServiceRouter.routeAndProcess(requestDTO);
    }
}

三、核心流程说明

  1. 启动初始化:
    Spring 启动时,BusinessServiceRouter 通过 ApplicationContextAware 扫描所有实现 BusinessProcessService 且带 @BusinessServiceMapping 的 Service,建立 “业务标识→Service” 的映射(如 DC→CustomerBusinessServiceORD→OrderBusinessService)。
  2. 请求处理:
    • 前端发送请求(如客户业务),businessCode=DCbusinessData={"customerName":"张三","phone":"13800138000"}
    • BusinessController 接收请求,调用 BusinessServiceRouter.routeAndProcess
    • 路由服务根据 businessCode=DC 找到 CustomerBusinessService,调用其 process 方法;
    • 处理完成后,返回统一格式的 BusinessResponseDTO

四、扩展与优势

  1. 新增业务:
    若需新增 “产品业务”(标识 PROD),只需:
    • 新建 ProductBusinessService 实现 BusinessProcessService
    • 用 @BusinessServiceMapping(value = "PROD", desc = "产品业务") 标记;
    • 无需修改 Controller 或路由服务,实现 “开闭原则”。
  2. 避免硬编码:
    传统写法需用 if (businessCode.equals("DC")) { 客户Service } else if (...),新增业务需修改代码;此方案通过注解 + 路由,彻底消除硬编码分支。
  3. 便于维护:
    每个业务 Service 职责单一,通过注解清晰标识业务范围,后续维护只需定位到对应 Service 即可。

五、依赖与配置

需确保 Spring 能扫描到注解标记的类,在 Spring Boot 启动类添加扫描路径(默认扫描启动类所在包及子包,若 Service 在其他包需手动指定):
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
// 若Service在com.yourproject.service包下,默认会扫描,无需额外配置;若路径不同需添加:
// @ComponentScan(basePackages = {"com.yourproject.controller", "com.yourproject.service"})
public class BusinessApplication {
    public static void main(String[] args) {
        SpringApplication.run(BusinessApplication.class, args);
    }
}

 

 

posted @ 2025-09-19 11:39  白玉神驹  阅读(10)  评论(0)    收藏  举报