如何在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
相关产品推荐
相关产品推荐

