Filed under arch

Apache Wink代码阅读 – 请求处理模式

Apache Wink是个实现REST服务的框架,本身基于Servlet,逻辑比较简单。其核心为HTTP Request的 一个处理过程:1)RestServlet收到request;2)使用Bean Factory创建一个Resource Bean;3)给bean赋值;4)调用Bean相应方法;5)返回response。

这里面跟一般的Servlet应用区别的是使用Resouce Bean作为Controller,对应Struts中的Action Bean。不同的是REST使用了Annotation来注入上下文,下面的一个Resource Bean就注入了三个属性,而Struts直接用参数来传递request, response信息。

@Path("/person")
public class PersonResource{
  @Context
  UriInfo uriInfo;
  @Context
  HttpServletResponse resp;
  @Context
  HttpServletRequest req;

  @GET
  @Produces(MediaType.APPLICATION_JSON)
  public JSONObject getPerson(@QueryParam("id") String id)  {
    String path = req.getPathInfo();
    ......
  }
}

所以Resouce Bean是有状态的么?如此一来岂不比单实例多线程的Servlet模式效率要差?难道跟Spring一样使用ThreadLocal?(后来证明这种主动的思考模式很有效,赞!)

查找代码,发现类RuntimeContextTLS(org.apache.wink.common.internal.runtime)将每个request处理线程(Servlet thread)需要的RuntimeContext保存在ThreadLocal成员变量中。类CreationUtils( org.apache.wink.common.internal.lifecycle)的方法createObject创建Resource实例并且inject属性(放在RuntimeContext中)。

ThreadLocal的特征使多线程的处理模式中不必一直携带Context,也就是即使Resource Bean只有一个对象实例,在多个线程调用其方法时,也不会出现对http request数据访问的同步问题,而annotation inject也使Resource Bean的方法定义中不必携带Context(因为是类的成员变量),这个跟Spring处理JDBC connection一样。

Wink可以常用DI框架比如Spring, Guice集成起来。比如在wink-spring-support这个子project中,类SpringLifecycleManager(org.apache.wink.spring.internal)使用Spring本身的object factory来管理Resource Bean,然后类DependenciesInjectionPostProcessor会使用上面提到的CreationUtils来注入context。

上面的分析没有考虑Wink有自己扩展的annotation:Scope,它模仿了Spring,分singleton和prototype两种(参见Wink的user guide)。

REST的资源定位

Story是这样的:客户查找书籍,server返回一堆书,每本书包含一个书的详细信息的href。假设我有“书的详细信息”这样一个resource:

@GET @Path(“/book/{library}/{bookId}”)
public Response getBookInfo(…)

这样在server端,我们就需要build这样一个URI。通常做法是hard coding,REST(这里是JAX-RS)则使用这样的方法:

UriBuilder ub = uriInfo.getBaseUriBuilder().path(BookResource.class).path(BookResource.class, “getBookInfo”);
URI uri = ub.build(aLibrary, aBookId);

第一行代码中的getBookInfo是方法名。

个人觉得这种方式有些新意。当年Struts就是靠灵活的跳转机制流行一时,REST则充分利用了anotation,避免使用复杂的配置文件,而且代码上也比hard coding的URL更有维护性和可读性。当然,如果getBookInfo不用字符串则更能体现静态语言的优点了。

那么,为什么“资源的定位”对于REST很重要呢?

对于一个Web应用,URI是很重要的一部分,这包括他的schema定义、URI的build以及路由,涉及到web开发的方方面面。REST里面有个概念hypermedia as the engine of application state。以上面的例子为例,hypermedia指的是server端返回的一个指向书详细信息的link,这个link是在server端construct的,client端拿到这个link后,就知道下一步跳转的方向,这样flow运行下去。整个应用就如同一状态机一样,从一个状态跳到另外一个状态。如果拿到的比如是孤立的信息,比如aLibery和aBookId,这时候客户端就要知道如何build link,而这一般都不是很容易的事情,也增加了系统的耦合度。而server端build href后,URI schema变化的代价就更小。这样我们就不必拘泥于web 2.0所说的:一个href应该永远可以访问。

Tagged