For the sake of discussion, consider a Resource /orders. One would typically POST to the resource to create an Order, update the Order at the Resource /orders/{id}, GET the Resource from the URI /orders/{id} and also delete the order at the Resource /orders/{id}.
One can therefore state that /orders/{id} is a Sub-Resource of /orders.
In jax-rs, sub-resources are obtained from the parent resource. A parent resource will not define a HTTP method for the sub-resource, but instead simple have a @Path annotation indicating a sub-resource.
For example,
@Path("/orders")
@Component
@Scope("singleton")
public class OrdersResource {
@Context
private ResourceContext resourceContext;
@Path("/{id}")
public OrderResource subResource(@PathParam("id") String id) {
OrderResource resource = resourceContext.getResource(OrderResource.class);
resource.setOrderId(Long.parseLong(id));
return resource;
}
...
}
In the above example, we have an OrdersResource that services the URI pattern of /orders. Calls to /orders/{id} are delegated to a sub-resource of the OrdersResource called OrderResource as shown below:
@PerRequest
@Component
@Scope("prototype")
public class OrderResource {
private Long orderId;
// Jersey injected
@Context
private ResourceContext resourceContext;
// Spring injected
@Autowired
private OrderService orderService;
@Autowired
private MapperIF beanMapper;
....
public void setOrderId(Long orderId) {
this.orderId = orderId;
}
// @Context are Jersey injected
@GET
@Produces("application/xml")
public Response getOrder(@Context HttpHeaders hh,
@Context Request request) { ... }
@Consumes("application/xml")
@PUT
public void updateOrder(@PathParam("id") String id, OrderDto orderDto) {
....
}
@DELETE
public void deleteOrder(@PathParam("id") String id) {
....
}
@Path("/lineItems")
public LineItemListDto getLineItems() throws OrderNotFoundException {
.....
}
}
Both the above mentioned Resources are beautifully managed by Spring and Jersey interoperability. Jersey Resources are injected by Jersey and Spring resources are injected by Spring auto-wiring. So cool!!!!
As shown above the OrderResource is following a Prototype pattern and is obtained via a called to resourceContext.getResource(OrderResource.class).
I quite like the fact that the OrderResource is declared as sub-resource of the OrdersResource and the creation of the Sub-Resource via the prototype pattern matches so beautifully within jax-rs. It becomes a trivial exercise to trace the flow of a uri-call from the parent resource to sub-resources.
5 comments:
// Spring injected
@Autowired
private OrderService orderService;
Should the OrderService be "
@Component
@Scope("prototype")"?
I have these "
AutoWired
UserDao userDao;"
in my codes, but the UserDao can't be injected.What problem?
OrderService is a singleton. Is your scanner scanning for the package for UserDao, does UserDao have @Component?
Excellent work!
Thank you for your nice work and explanation. I have tried to find out the way to handle sub-resouces with Spring for two days. But I coudln't find it. I think I should google and find this blog.
Is this still valid for resource/sub-resource relationships? It's an old article,
In your example, there is a slightly better way to do it, in that you don't specify the {id} on the sub-resource. You add it to the path of the sub-resource, *in* the sub-resource where it belongs.
This means that the paths move down a level, with the OrderResource having a @GET @Path("/{id}") annotated method. This has a few advantages, primary being that the {id} isn't passed in as a property, it's simply part fo the resource path of the sub-resource. Another advantage is that its easy to version your root now, without removing the old versions. you would simply return the same OrderResource in /v2 if hadn't changes, and a new OrderResource in /3 if it does. (especially useful in mobile endpoints)
@Path("/v1")
@Component
@Scope("singleton")
public class OrdersResource {
@Context
private ResourceContext resourceContext;
@Path("/orders")
public OrderResource subResource() {
return resourceContext.getResource(OrderResource.class);
}
...
}
Post a Comment