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"; } }
方案优势分析
- 极致代码复用:将重复的CRUD逻辑集中到抽象类,子类仅需实现差异化配置,避免大量复制粘贴的冗余代码。
- 高扩展性:后续新增任何具备ID的实体(如Book、Order),只需实现
BaseEntity接口并继承BaseCrudController,几行代码即可完成基础CRUD接口开发。 - 易维护性:如果需要修改CRUD逻辑(如添加参数校验、操作日志),仅需在抽象类中修改一次,所有子类自动同步更新,避免逐个修改控制器的繁琐操作。
- 逻辑清晰:抽象类负责通用逻辑,子类专注于业务差异化,代码职责划分明确,可读性更强。
其他方案对比
- 复制粘贴代码:完全不可取,冗余度极高,维护成本指数级上升。
- AOP切面:适合处理日志、权限等横切关注点,但对于核心CRUD业务逻辑的复用,不如抽象类直接直观,且增加了复杂度。
- Spring Data REST:适合快速生成REST接口,但依赖Spring Data生态,对于当前简单的内存存储场景,泛型抽象类更轻量灵活,无需引入额外依赖。
内容的提问来源于stack exchange,提问作者ANVISERO
相关产品推荐
相关产品推荐

