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

Spring Boot REST CRUD API中抽象/泛型类使用问题求助

Spring Boot JPA 抽象类复用Repository时Bean冲突问题修复

项目背景

原本运行正常的Spring Boot MySQL CRUD项目,重构为抽象类Employee + 实体类Clerk/Admin的OOP结构后启动失败,核心需求是复用JpaRepository的findAll等方法,避免重复编写代码。

项目结构

src/main/java/dev.cherry.emp/
├── Controller
│   ├── AdminController.java
│   ├── ClerkController.java
│   └── EmployeeController.java
├── EmpApplication.java
├── Employee
│   └── Util
│       ├── Employee.java
│       └── Gender.java
├── Model
│   ├── Admin.java
│   └── Clerk.java
├── Repository
│   ├── AdminRepository.java
│   ├── ClerkRepository.java
│   └── EmployeeRepository.java
└── Service
    ├── AdminService.java
    ├── ClerkService.java
    └── EmployeeService.java

核心代码(原错误版本)

抽象类Employee

package dev.cherry.emp.Employee.Util;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@MappedSuperclass
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public abstract class Employee {

    @Id
    @Column(name = "emp_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long empId;
}

抽象Repository

package dev.cherry.emp.Repository;

import dev.cherry.emp.Employee.Util.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

@NoRepositoryBean
public interface EmployeeRepository<T extends Employee> extends JpaRepository<T, Long> {
}

原错误Service类

package dev.cherry.emp.Service;

import dev.cherry.emp.Repository.EmployeeRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class EmployeeService<T> {

    private final EmployeeRepository employeeRepo;

    @Autowired
    public EmployeeService(EmployeeRepository employeeRepo) {
        this.employeeRepo = employeeRepo;
    }

    public List<T> allEmps() {
        return (List<T>) employeeRepo.findAll();
    }
}

原错误Controller类

package dev.cherry.emp.Controller;

import dev.cherry.emp.Service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class EmployeeController<T> {

    private final EmployeeService employeeService;

    @Autowired
    public EmployeeController(EmployeeService employeeService) {
        this.employeeService = employeeService;
    }

    public ResponseEntity<List<T>> allEmps() {
        List<T> allEmps = employeeService.allEmps();
        return new ResponseEntity<>(allEmps, HttpStatus.OK);
    }
}

报错信息

应用启动失败

描述:
dev.cherry.emp.Service.EmployeeService构造器的第0个参数需要单个Bean,但找到2个:

  • adminRepository:定义在dev.cherry.emp.Repository.AdminRepository,由@EnableJpaRepositories声明于JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration
  • clerkRepository:定义在dev.cherry.emp.Repository.ClerkRepository,由@EnableJpaRepositories声明于JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration

操作建议:
考虑将其中一个Bean标记为@Primary,更新消费者以接受多个Bean,或使用@Qualifier指定要使用的Bean

错误原因

EmployeeService被标记为@Service,Spring会尝试实例化它,但它依赖的EmployeeRepository有两个具体实现(AdminRepository和ClerkRepository),Spring无法自动确定注入哪一个,导致Bean歧义冲突。

修复方案

通过将通用Service和Controller改为抽象类,让具体子类(Admin/Clerk对应的Service/Controller)分别绑定各自的Repository,消除歧义,同时保留代码复用能力。

1. 修改EmployeeService为泛型抽象类

移除@Service注解,让子类负责实例化:

package dev.cherry.emp.Service;

import dev.cherry.emp.Employee.Util.Employee;
import dev.cherry.emp.Repository.EmployeeRepository;
import java.util.List;

public abstract class EmployeeService<T extends Employee> {

    protected final EmployeeRepository<T, Long> employeeRepo;

    public EmployeeService(EmployeeRepository<T, Long> employeeRepo) {
        this.employeeRepo = employeeRepo;
    }

    public List<T> allEmps() {
        return employeeRepo.findAll();
    }
}

2. 实现具体Service子类

AdminService

package dev.cherry.emp.Service;

import dev.cherry.emp.Model.Admin;
import dev.cherry.emp.Repository.AdminRepository;
import org.springframework.stereotype.Service;

@Service
public class AdminService extends EmployeeService<Admin> {

    public AdminService(AdminRepository adminRepository) {
        super(adminRepository);
    }
}

ClerkService

package dev.cherry.emp.Service;

import dev.cherry.emp.Model.Clerk;
import dev.cherry.emp.Repository.ClerkRepository;
import org.springframework.stereotype.Service;

@Service
public class ClerkService extends EmployeeService<Clerk> {

    public ClerkService(ClerkRepository clerkRepository) {
        super(clerkRepository);
    }
}

3. 修改EmployeeController为泛型抽象类

移除@RestController注解,让子类实现具体请求映射:

package dev.cherry.emp.Controller;

import dev.cherry.emp.Employee.Util.Employee;
import dev.cherry.emp.Service.EmployeeService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.List;

public abstract class EmployeeController<T extends Employee> {

    protected final EmployeeService<T> employeeService;

    public EmployeeController(EmployeeService<T> employeeService) {
        this.employeeService = employeeService;
    }

    public ResponseEntity<List<T>> allEmps() {
        List<T> allEmps = employeeService.allEmps();
        return new ResponseEntity<>(allEmps, HttpStatus.OK);
    }
}

4. 实现具体Controller子类

AdminController

package dev.cherry.emp.Controller;

import dev.cherry.emp.Model.Admin;
import dev.cherry.emp.Service.AdminService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admins")
public class AdminController extends EmployeeController<Admin> {

    public AdminController(AdminService adminService) {
        super(adminService);
    }

    @GetMapping
    @Override
    public ResponseEntity<List<Admin>> allEmps() {
        return super.allEmps();
    }
}

ClerkController

package dev.cherry.emp.Controller;

import dev.cherry.emp.Model.Clerk;
import dev.cherry.emp.Service.ClerkService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/clerks")
public class ClerkController extends EmployeeController<Clerk> {

    public ClerkController(ClerkService clerkService) {
        super(clerkService);
    }

    @GetMapping
    @Override
    public ResponseEntity<List<Clerk>> allEmps() {
        return super.allEmps();
    }
}

5. 确保Repository子类正确继承

AdminRepository和ClerkRepository需继承泛型EmployeeRepository:

AdminRepository

package dev.cherry.emp.Repository;

import dev.cherry.emp.Model.Admin;
import org.springframework.stereotype.Repository;

@Repository
public interface AdminRepository extends EmployeeRepository<Admin, Long> {
}

ClerkRepository

package dev.cherry.emp.Repository;

import dev.cherry.emp.Model.Clerk;
import org.springframework.stereotype.Repository;

@Repository
public interface ClerkRepository extends EmployeeRepository<Clerk, Long> {
}

修复原理

  • 抽象的EmployeeService和EmployeeController不再被Spring直接实例化,避免了依赖注入时的Bean歧义
  • 具体子类(AdminService/ClerkService)分别注入各自对应的Repository,Spring可以明确识别要注入的Bean
  • 通过继承关系复用了findAll等通用方法,达成了代码复用的目标

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 03:46:08