我认为这是一个更通用的java问题,但我会解释我正在尝试做的事情,希望有人能给我指出正确的方法;
我正在尝试创建一个通用抽象类,我的所有资源都可以从中扩展。
抽象类具有标准内容的基本 CRUD 实现
@Produces("application/vnd.api+json")
@Consumes("application/vnd.api+json")
public abstract class AbstractResource {
static final Logger LOGGER = LoggerFactory.getLogger(AbstractResource.class);
AbstractRepository repository;
AbstractResource(AbstractRepository repository) {
this.repository = repository;
}
@GET
public Response getAll(@Auth User user, @QueryParam("query") String query) {
String result = query != null ? repository.getByQuery(query) : repository.getAll();
return Response.status(Response.Status.OK).entity(result).build();
}
@GET
@Path("/{id}")
public Response getById(@Auth User user, @PathParam("id") String id) {
String result = repository.getById(id);
return Response.status(Response.Status.OK).entity(result).build();
}
@POST
public Response save(@Auth User user, String payload) {
String result = repository.save(payload);
return Response.status(Response.Status.OK).entity(result).build();
}
@PATCH
@Path("/{id}")
public Response update(@Auth User user, @PathParam("id") String id, String payload) {
String result = repository.update(payload);
return Response.status(Response.Status.OK).entity(result).build();
}
@DELETE
@Path("/{id}")
public Response delete(@Auth User user, @PathParam("id") String id) {
repository.delete(id);
return Response.status(Response.Status.NO_CONTENT).build();
}
}
我可以毫无问题地使用它
@Path("/movies")
public class MovieResource extends AbstractResource {
public MovieResource(MovieRepository repository) {
super(repository);
}
}
我现在可以访问所有方法并根据需要覆盖。
我遇到问题的地方是当我需要重载一个方法时。以抽象类中的第一个 getAll
方法为例,我只想更改 Movie.class
@Path("/movies")
public class MovieResource extends AbstractResource {
public MovieResource(MovieRepository repository) {
super(repository);
}
@GET
public Response getAll(@Auth User user, @QueryParam("query") String query, @QueryParam("limit") String limit, @QueryParam("page") String page) {
String result = repository.getPaginated(limit, page);
return Response.status(Response.Status.OK).entity(result).build();
}
}
因此,getAll
方法在 Movie.class
中具有一组不同的参数。这会导致 Jersey 爆炸
[[FATAL] A resource model has ambiguous (sub-)resource method for HTTP method GET and input mime-types as defined by"@Consumes" and "@Produces" annotations at Java methods public javax.ws.rs.core.Response space.cuttlefish.domain.resources.MovieResource.getAll(space.cuttlefish.domain.model.User,java.lang.String,java.lang.String,java.lang.String) and public javax.ws.rs.core.Response space.cuttlefish.domain.resources.AbstractResource.getAll(space.cuttlefish.domain.model.User,java.lang.String) at matching regular expression /movies. These two methods produces and consumes exactly the same mime-types and therefore their invocation as a resource methods will always fail.; source='org.glassfish.jersey.server.model.RuntimeResource@6a1ef65c']
因为抽象的原始getAll
方法已经有了@GET
注解。
那么,我该如何解决这个问题呢?
我是否从抽象类中删除所有注释,然后必须在每个资源中覆盖并重新添加注释?这看起来很困惑并且容易出错......这里一定有更好的解决方案吗?
有什么明显的事情我刚刚忽略了吗?
希望得到一些帮助!
最佳答案
我建议使用泛型。
我们已经完成了一个类似但相当复杂的版本。一开始要做好有点困难,但我们拥有最大的代码可重用性(使用 Java)并且易于阅读/贡献代码。
public abstract class AbstractResource<T extends AbstractObject, K extends AbstractObjectDto> {
static final Logger LOGGER = LoggerFactory.getLogger(AbstractResource.class);
AbstractRepository<T> repository;
// We have used modelmapper library to automatically convert DTO objects to database objects. But you can come up with your own solution for that. I.E implementing conversion logic on each DTO and database classes.
ModelMapper modelMapper = new ModelMapper();
// With Java Generics, one cannot access the class type directly by simply calling 'K.class'. So you need to pass the class types explicitly as well. That is if you're using modelmapper.
private final Class<T> objectClass;
private final Class<K> objectDtoClass;
AbstractResource(AbstractRepository<T> repository, Class<T> objectClass, Class<K> objectDtoClass) {
this.repository = repository;
this.objectClass = objectClass;
this.objectDtoClass = objectDtoClass;
}
...
@POST
public K save(@Auth User user, @Valid K payload) {
T databaseObject = modelmapper.map(payload, objectClass);
T result = repository.save(databaseObject);
K resultDto = modelMapper.map(result, objectDtoClass);
retun resultDto;
}
...
}
然后,您需要为每种对象类型创建一个具有必要方法的存储库类,例如 save
、getPaginated
等,并重写 AbstractRepository
。当然,Movie
应该扩展 AbstractObject
类,MovieDto
应该扩展 AbstractObjectDto
类。
public class MovieRepository extends AbstractRepository<Movie> {
....
Movie save(Movie movie) {...}
}
剩下的就这么简单:
@Path("/movies")
public class MovieResource extends AbstractResource<Movie, MovieDto> {
public MovieResource(MovieRepository repository) {
super(repository, Movie.class, MovieDto.class);
}
}
关于java - Dropwizard 抽象资源设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41403664/