I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an ex
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a @Service
or @Repository
, then those too must be generic.
You might have a generic controller:
abstract class GenericController {
public abstract GenericService getService();
@GetMapping
public ResponseEntity> findAll() {
return ResponseEntity.ok(getService().findAll());
}
@PostMapping
public ResponseEntity 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 {
@Autowired CategoryServ serv;
@Override
public GenericService getService() {
return serv;
}
}
@Controller
@RequestMapping("/product")
class ProductContr extends GenericController {
@Autowired ProductServ serv;
@Override
public GenericService getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService {
public abstract GenericRepository getRepository();
public Iterable findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
@Service
class CategoryServ extends GenericService {
@Autowired CategoryRepo repo;
@Override
public GenericRepository getRepository() {
return repo;
}
}
@Service
class ProductServ extends GenericService {
@Autowired ProductRepo repo;
@Override
public GenericRepository getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
@NoRepositoryBean
interface GenericRepository extends JpaRepository {
}
@Repository
interface CategoryRepo extends GenericRepository {
}
@Repository
interface ProductRepo extends GenericRepository {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with @RequestMapping
automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added @RequestMapping
annotation and then call the super class method.
Another approach is using annotations.