Это хорошее время для использования наследования и Java Generics.Стоит отметить, что если у вашего контроллера есть какие-либо зависимости, такие как @Service
или @Repository
, то они тоже должны быть общими.
У вас может быть универсальный контроллер:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
@GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// @DeleteMapping, @PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
@Controller
@RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
@Autowired CategoryServ serv;
@Override
public GenericService<Category> getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController<Product> {
@Autowired ProductServ serv;
@Override
public GenericService<Product> getService() {
return serv;
}
}
Затем вы должны иметь абстрактные версии зависимостей.Сервисы:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService<Category> {
@Autowired CategoryRepo repo;
@Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService<Product> {
@Autowired ProductRepo repo;
@Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Затем сервисы также имеют свои зависимости - репозитории:
@NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
@Repository
interface CategoryRepo extends GenericRepository<Category> {
}
@Repository
interface ProductRepo extends GenericRepository<Product> {
}
Это был мой первый подход.Это работает очень хорошо.Однако это создает прочную связь между бизнес-логикой каждого сервиса и универсального сервиса.То же самое относится и к универсальному контроллеру и его дочерним классам.Конечно, вы всегда можете переопределить определенную операцию CRUD.Но вы должны делать это с осторожностью, поскольку вы можете создать неожиданное поведение.Стоит также отметить, что наследование от классов, которые имеют методы, аннотированные @RequestMapping
, автоматически предоставляет все аннотированные методы.Это может быть нежелательно.Например, нам может не потребоваться опция удаления для категорий, но мы хотим ее для товаров.Чтобы бороться с этим, вместо того, чтобы аннотировать метод в родительском классе, мы можем просто определить его в родительском классе и переопределить нужные операции CRUD с добавленной аннотацией @RequestMapping
, а затем вызвать метод суперкласса.
Другой подход - с использованием аннотаций.