You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spring Boot服务中正确应用多态实现PDF生成?

在Spring Boot中正确应用多态实现PDF生成服务

1. 重构泛型接口与抽象基类(统一模板+多态结合)

先把PDFService改为泛型接口明确操作的实体类型,再通过抽象基类抽离公共逻辑,让子类只关注差异化业务:

泛型PDFService接口

public interface PDFService<T> {
    String createPdf(UUID uuid);
}

抽象基类(模板方法+多态)

public abstract class AbstractPDFService<T> implements PDFService<T> {

    // 统一流程的模板方法,子类仅需实现差异化步骤
    @Override
    public String createPdf(UUID uuid) {
        // 公共步骤:获取实体
        T entity = getEntityByUuid(uuid);
        // 公共步骤:通用校验(可根据需求调整)
        validateEntity(entity);
        // 差异化步骤:生成对应HTML
        return generateHtml(entity);
    }

    // 子类实现:获取对应实体
    protected abstract T getEntityByUuid(UUID uuid);

    // 子类实现:生成对应实体的HTML
    protected abstract String generateHtml(T entity);

    // 通用实体校验逻辑
    private void validateEntity(T entity) {
        if (entity == null) {
            throw new IllegalArgumentException("实体不存在,UUID: " + uuid);
        }
    }
}

2. 实现子类(专注差异化业务)

BrandPDFService实现

@Service("brandPDFService")
@RequiredArgsConstructor
public class BrandPDFService extends AbstractPDFService<Brand> {

    private final BrandService brandService;

    @Override
    protected Brand getEntityByUuid(UUID uuid) {
        return brandService.findByUuid(uuid);
    }

    @Override
    protected String generateHtml(Brand brand) {
        // 品牌专属HTML生成逻辑
        return "<html><body>品牌名称:" + brand.getName() + "</body></html>";
    }
}

ProductPDFService实现

@Service("productPDFService")
@RequiredArgsConstructor
public class ProductPDFService extends AbstractPDFService<Product> {

    private final ProductService productService;

    @Override
    protected Product getEntityByUuid(UUID uuid) {
        return productService.findByUuid(uuid);
    }

    @Override
    protected String generateHtml(Product product) {
        // 产品专属HTML生成逻辑
        return "<html><body>产品名称:" + product.getName() + "</body></html>";
    }
}

3. 正确使用多态(Spring依赖注入)

Spring环境下禁止手动new实例,通过依赖注入获取实现类,两种常用方式:

方式1:按类型/名称注入(静态场景)

在需要使用的类中,直接注入对应类型的PDFService:

@Service
@RequiredArgsConstructor
public class BusinessService {

    // Spring会根据泛型自动匹配BrandPDFService
    private final PDFService<Brand> brandPDFService;
    // Spring会根据泛型自动匹配ProductPDFService
    private final PDFService<Product> productPDFService;

    public void generateBrandPdf(UUID uuid) {
        String html = brandPDFService.createPdf(uuid);
        // 后续PDF生成逻辑
    }

    public void generateProductPdf(UUID uuid) {
        String html = productPDFService.createPdf(uuid);
        // 后续PDF生成逻辑
    }
}

若存在类型歧义,可配合@Qualifier("brandPDFService")明确指定注入的Bean名称。

方式2:工厂模式动态获取(动态场景)

如果需要根据实体类型动态获取PDFService,可以创建工厂类:

@Service
public class PDFServiceFactory {

    private final Map<Class<?>, PDFService<?>> pdfServiceMap;

    // Spring自动注入所有PDFService实现类到Map中
    public PDFServiceFactory(List<PDFService<?>> pdfServices) {
        this.pdfServiceMap = pdfServices.stream()
                .collect(Collectors.toMap(
                        service -> {
                            // 通过反射获取泛型对应的实体类型
                            Type genericSuperclass = service.getClass().getGenericSuperclass();
                            if (genericSuperclass instanceof ParameterizedType) {
                                return (Class<?>) ((ParameterizedType) genericSuperclass).getActualTypeArguments()[0];
                            }
                            throw new IllegalArgumentException("PDFService实现类未指定泛型类型");
                        },
                        Function.identity()
                ));
    }

    @SuppressWarnings("unchecked")
    public <T> PDFService<T> getPDFService(Class<T> entityClass) {
        PDFService<?> service = pdfServiceMap.get(entityClass);
        if (service == null) {
            throw new IllegalArgumentException("无对应实体的PDFService实现:" + entityClass.getName());
        }
        return (PDFService<T>) service;
    }
}

使用示例:

@Service
@RequiredArgsConstructor
public class DynamicService {

    private final PDFServiceFactory pdfServiceFactory;

    public void generatePdf(Class<?> entityClass, UUID uuid) {
        PDFService<?> pdfService = pdfServiceFactory.getPDFService(entityClass);
        String html = pdfService.createPdf(uuid);
        // 后续PDF生成逻辑
    }

    // 调用示例
    public void testGenerate() {
        generatePdf(Brand.class, UUID.randomUUID());
        generatePdf(Product.class, UUID.randomUUID());
    }
}

4. 方案优势

  • 多态落地:所有实现类遵循统一接口,通过父类类型引用子类对象,统一调用createPdf方法。
  • 减少重复代码:抽象基类抽离公共流程,子类仅需实现差异化逻辑。
  • Spring适配:完全依赖Spring的依赖注入管理,避免手动实例化的资源问题。

内容的提问来源于stack exchange,提问作者user19540291

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 15:39:37