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

Spring Boot中如何用带泛型的抽象类复用控制器逻辑?

用泛型抽象类简化Spring Boot相似控制器的实现

核心结论

完全可以通过带泛型的抽象类实现相似控制器的逻辑复用,这是当前场景下最优的方案之一,既能大幅减少冗余代码,又能提升后续的扩展性与维护性。

实现步骤

1. 定义通用实体接口

首先需要确保所有实体(Film、User)都具备统一的ID获取方法,定义一个BaseEntity接口:

public interface BaseEntity {
    Integer getId();
}

让Film和User实现该接口:

// Film类示例
public class Film implements BaseEntity {
    private Integer id;
    // 其他业务字段、getter/setter方法
    @Override
    public Integer getId() {
        return id;
    }
}

// User类示例
public class User implements BaseEntity {
    private Integer id;
    // 其他业务字段、getter/setter方法
    @Override
    public Integer getId() {
        return id;
    }
}

2. 创建泛型抽象控制器

将两个控制器的通用CRUD逻辑抽离到抽象类中,通过抽象方法暴露差异化配置(存储Map、请求路径),利用Spring的SpEL表达式动态绑定请求路径:

public abstract class BaseCrudController<T extends BaseEntity> {

    // 子类需提供自己的内存存储Map
    protected abstract Map<Integer, T> getEntityMap();

    // 子类需提供单实体操作的请求路径(如"/film"、"/user")
    protected abstract String getSingleEntityPath();

    // 子类需提供实体集合的请求路径(如"/films"、"/users")
    protected abstract String getEntityCollectionPath();

    @PostMapping("#{getSingleEntityPath()}")
    public T createEntity(@RequestBody T entity) {
        getEntityMap().put(entity.getId(), entity);
        return entity;
    }

    @PutMapping("#{getSingleEntityPath()}")
    public T updateEntity(@RequestBody T entity) {
        getEntityMap().put(entity.getId(), entity);
        return entity;
    }

    @GetMapping("#{getEntityCollectionPath()}")
    public List<T> getAllEntities() {
        return new ArrayList<>(getEntityMap().values());
    }
}

3. 实现具体控制器

每个业务控制器只需继承抽象类,实现抽象方法即可完成功能,无需重复编写CRUD逻辑:

@RestController
public class FilmController extends BaseCrudController<Film> {
    // 私有内存存储,仅当前控制器使用
    private static final Map<Integer, Film> films = new HashMap<>();

    @Override
    protected Map<Integer, Film> getEntityMap() {
        return films;
    }

    @Override
    protected String getSingleEntityPath() {
        return "/film";
    }

    @Override
    protected String getEntityCollectionPath() {
        return "/films";
    }
}
@RestController
public class UserController extends BaseCrudController<User> {
    private static final Map<Integer, User> users = new HashMap<>();

    @Override
    protected Map<Integer, User> getEntityMap() {
        return users;
    }

    @Override
    protected String getSingleEntityPath() {
        return "/user";
    }

    @Override
    protected String getEntityCollectionPath() {
        return "/users";
    }
}

方案优势分析

  1. 极致代码复用:将重复的CRUD逻辑集中到抽象类,子类仅需实现差异化配置,避免大量复制粘贴的冗余代码。
  2. 高扩展性:后续新增任何具备ID的实体(如Book、Order),只需实现BaseEntity接口并继承BaseCrudController,几行代码即可完成基础CRUD接口开发。
  3. 易维护性:如果需要修改CRUD逻辑(如添加参数校验、操作日志),仅需在抽象类中修改一次,所有子类自动同步更新,避免逐个修改控制器的繁琐操作。
  4. 逻辑清晰:抽象类负责通用逻辑,子类专注于业务差异化,代码职责划分明确,可读性更强。

其他方案对比

  • 复制粘贴代码:完全不可取,冗余度极高,维护成本指数级上升。
  • AOP切面:适合处理日志、权限等横切关注点,但对于核心CRUD业务逻辑的复用,不如抽象类直接直观,且增加了复杂度。
  • Spring Data REST:适合快速生成REST接口,但依赖Spring Data生态,对于当前简单的内存存储场景,泛型抽象类更轻量灵活,无需引入额外依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 00:20:57