java - Dropwizard 抽象资源设计

标签 java design-patterns jax-rs abstract-class dropwizard

我认为这是一个更通用的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;
    }
    ...
}

然后,您需要为每种对象类型创建一个具有必要方法的存储库类,例如 savegetPaginated 等,并重写 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/

相关文章:

asp.net-mvc - 设计模式 : Which is most appropriate to use for a wizard style user interface?

java - 是否有任何标准方法可以将对象转换为不同的对象?

java - 在同一方法上使用 Jax-Rs @Get @Post

java - 基于 jax-rs 的框架应该使用单个异常映射器吗?

java - 有没有办法在 Android 上与 USB 设备通信?

java - 在java中使用mockito测试sql查询时出错

SceneBuilder 中的 JavaFX 和 TableView

java - java中如何将String数组改为ArrayList

.net - 在哪里对实体运行重复检查

java - 如何限制jpa查询