JAX-RS 在 REST 服务中的可重用性
Reusability in REST services with JAX-RS
我有一个简单的类似 REST 的服务,在 JAX-RS 中实现以维护 Foo
。它使用 POST foos
创建 foos,使用 GET foos
列出 foos 并使用 GET foo/42
详细说明给定的 foo(其中 42 是给定的 foo 的 ID)。
public class FooService {
@POST
@Path("/foos")
public Foo create(Foo foo){
entityManager.getTransaction().begin();
entityManager.persist(foo);
entityManager.getTransaction().commit();
return foo;
}
@GET
@Path("/foos")
public List<Foo> get(){
List<Foo> foos = //more-or-less generic JPA querying-code
return foos;
}
@GET
@Path("/foos/{id}")
public Foo get(@PathParam("id") int id){
Foo foo = //more-or-less generic JPA querying-code
return foo;
}
}
现在,如果我也有维护 Bar
的类似服务,我应该如何优雅地避免代码重复?
public class BarService {
@POST
@Path("/bars")
public Bar create(Bar bar){
entityManager.getTransaction().begin();
entityManager.persist(bar);
entityManager.getTransaction().commit();
return bar;
}
@GET
@Path("/bars")
public List<Bar> get(){
List<Bar> bars = //more-or-less generic JPA querying-code
return bars;
}
@GET
@Path("/bars/{id}")
public Bar get(@PathParam("id") int id){
Bar bar = //more-or-less generic JPA querying-code
return bar;
}
}
唯一的区别是在 @Path
注释中指定的 path 值。因为这个值应该是静态的(在编译时可见)所以不可能创建一个 AbstracService
class 像:
public abstract class AbstracService<X> {
//abstrac static is not possible
public abstract static final String PATH;
@POST
@Path(PATH)
public X create(X entity){ ... }
@GET
@Path(PATH)
public List<X> get(){ ... }
@GET
@Path(PATH + "/{id}")
public X get(@PathParam("id") int id){ ... }
}
public class FooService extends AbstractService<Foo>{
//just override the PATH to be "foos"
}
public class BarService extends AbstractService<Bar>{
//just override the PATH to be "bars"
}
我是否需要重写每个服务方法来调整 @Path
并调用 super
的实现?
上面的 class 和 FooService
和 BarService
太相似了,以至于我的可重用性问题保持沉默。
好吧,我相信你可以做这样的事情
@POST
@Path("/{foosOrBars}")//will match to any string
public BaseFooBar create(@PathParam("foosOrBars") String type){
if(type.equals("foos")){
//do something with the foo
}else{
//do something different
}
}
所以你有一个基础 class BaseFooBar
,你可以将其扩展为 Foo
或 Bar
。
但是你必须小心,如果你在同一个服务上有另一个方法,也只是具有相同的层次结构级别,例如/foobar.它应该有一个固定的标识符并且不能使用花括号,否则你的路径将无法正确匹配。
在我的资源中很常见class就是这种模式
@Path("foo")
public class FoosResource {
@GET // get all `Foo` resources
...
@GET
@Path("{id}") // get `Foo` with this id
...
@POST // create new `Foo` in `foo` collection
...
@PUT
@Path("{id}") // Update `Foo` with this id
}
你懂的。关键是集合的名称在资源class上,而不是像你拥有的资源方法级别
@Path("foo/{id}")
你无论如何都需要 @Path
来使 class 成为资源 class,为什么不使用最合适的名称呢。
我可能要补充的另一件事是我经常做的事情。我在摘要 class 中添加了一个 Class
字段,带有一个构造函数参数,我可以将 Class
传递给它。然后在具体的class实现中,我super(Foo.class)
。这样,JPA 就可以访问 class,这使得使用类型查询变得更加容易。
我有一个简单的类似 REST 的服务,在 JAX-RS 中实现以维护 Foo
。它使用 POST foos
创建 foos,使用 GET foos
列出 foos 并使用 GET foo/42
详细说明给定的 foo(其中 42 是给定的 foo 的 ID)。
public class FooService {
@POST
@Path("/foos")
public Foo create(Foo foo){
entityManager.getTransaction().begin();
entityManager.persist(foo);
entityManager.getTransaction().commit();
return foo;
}
@GET
@Path("/foos")
public List<Foo> get(){
List<Foo> foos = //more-or-less generic JPA querying-code
return foos;
}
@GET
@Path("/foos/{id}")
public Foo get(@PathParam("id") int id){
Foo foo = //more-or-less generic JPA querying-code
return foo;
}
}
现在,如果我也有维护 Bar
的类似服务,我应该如何优雅地避免代码重复?
public class BarService {
@POST
@Path("/bars")
public Bar create(Bar bar){
entityManager.getTransaction().begin();
entityManager.persist(bar);
entityManager.getTransaction().commit();
return bar;
}
@GET
@Path("/bars")
public List<Bar> get(){
List<Bar> bars = //more-or-less generic JPA querying-code
return bars;
}
@GET
@Path("/bars/{id}")
public Bar get(@PathParam("id") int id){
Bar bar = //more-or-less generic JPA querying-code
return bar;
}
}
唯一的区别是在 @Path
注释中指定的 path 值。因为这个值应该是静态的(在编译时可见)所以不可能创建一个 AbstracService
class 像:
public abstract class AbstracService<X> {
//abstrac static is not possible
public abstract static final String PATH;
@POST
@Path(PATH)
public X create(X entity){ ... }
@GET
@Path(PATH)
public List<X> get(){ ... }
@GET
@Path(PATH + "/{id}")
public X get(@PathParam("id") int id){ ... }
}
public class FooService extends AbstractService<Foo>{
//just override the PATH to be "foos"
}
public class BarService extends AbstractService<Bar>{
//just override the PATH to be "bars"
}
我是否需要重写每个服务方法来调整 @Path
并调用 super
的实现?
上面的 class 和 FooService
和 BarService
太相似了,以至于我的可重用性问题保持沉默。
好吧,我相信你可以做这样的事情
@POST
@Path("/{foosOrBars}")//will match to any string
public BaseFooBar create(@PathParam("foosOrBars") String type){
if(type.equals("foos")){
//do something with the foo
}else{
//do something different
}
}
所以你有一个基础 class BaseFooBar
,你可以将其扩展为 Foo
或 Bar
。
但是你必须小心,如果你在同一个服务上有另一个方法,也只是具有相同的层次结构级别,例如/foobar.它应该有一个固定的标识符并且不能使用花括号,否则你的路径将无法正确匹配。
在我的资源中很常见class就是这种模式
@Path("foo")
public class FoosResource {
@GET // get all `Foo` resources
...
@GET
@Path("{id}") // get `Foo` with this id
...
@POST // create new `Foo` in `foo` collection
...
@PUT
@Path("{id}") // Update `Foo` with this id
}
你懂的。关键是集合的名称在资源class上,而不是像你拥有的资源方法级别
@Path("foo/{id}")
你无论如何都需要 @Path
来使 class 成为资源 class,为什么不使用最合适的名称呢。
我可能要补充的另一件事是我经常做的事情。我在摘要 class 中添加了一个 Class
字段,带有一个构造函数参数,我可以将 Class
传递给它。然后在具体的class实现中,我super(Foo.class)
。这样,JPA 就可以访问 class,这使得使用类型查询变得更加容易。