Norman 昨天发布了 Seam 2.1.2,与之前的 2.1.x 版本相比,其对 REST 处理的支持有了很大改进。我们大约一年前开始将 RESTEasy(JAX-RS 的实现,JSR 311)与 Seam 集成,这是在一个 早期原型 中开始的。然后我们等待 JAX-RS 规范最终确定和 RESTEasy 发布 GA,这发生在几个月前。因此,基于这个稳定的基石,我们能够完成与 Seam 的集成。

我将在这里展示一些集成的一些独特功能,例如如何创建 RESTful Seam 应用程序或简单地向现有应用程序添加 HTTP 网络服务接口。

部署资源和提供者

使用 JAX-RS,您编写一个普通的 Java 类,并在其上放置@javax.ws.rs.Path("/customer")以使其在 HTTP 基础 URI 路径下可用/customer. 然后,您使用@javax.ws.rs.GET, @POST, @DELETE等注解将此类的方法映射到特定的子路径和 HTTP 方法。这些类称为 资源 类。实例的默认生命周期是每个 HTTP 请求,为请求创建一个实例,并在处理完成后销毁它,并发送响应。

HTTP 实体(HTTP 请求的主体)的转换是 提供者 类的工作,这些类注解了@javax.ws.rs.ext.Provider通常是无状态的或单例的。它们在 HTTP 和 Java 类型之间转换内容,例如将my.Customer实体转换为 XML,并使用 JAXB 从 XML 转换。提供者也是 JAX-RS 中自定义异常转换器等的扩展点。

RESTEasy 有自己的类路径扫描程序,通过查找注解来检测所有资源和提供者。这需要在web.xml还需要配置一个请求调度器servlet。最后,如果您想将资源类配置为EJB,以实现自动事务边界定义和持久化上下文处理,还必须在web.xml中列出这些EJB。这个最后的功能是RESTEasy的增强功能,而不是JAX-RS规范的一部分。

如果您使用Seam配合RESTEasy,则无需进行这些额外的工作。当然,它仍然需要完成,但你很可能已经配置了基本的Seam监听器和资源servlet,这在web.xml几乎所有Seam应用中都是。

您根本无需配置RESTEasy。只需放入正确的JAR文件(见参考文档),您的Seam应用将自动找到所有@Path资源@Provider。您的无状态EJB仍然需要列出以便找到,但这可以在Seam的组件.xml中完成,或者通过常规的Seam API进行程序化配置。所有其他RESTEasy配置选项和一些有用的其他配置功能也都可以使用。

因此,不更改任何代码,您就可以轻松部署并集成配置您的Seam应用中的JAX-RS工件。

利用Seam组件

资源和提供者可以成为Seam组件,具有注入、生命周期管理、授权、拦截等。只需在资源类上放置一个@Name即可。

@Name("customerResource")
@Scope(ScopeType.EVENT) // Default
@Path("/customer")
public class MyCustomerResource {

    @In
    CustomerDAO customerDAO;

    @GET
    @Path("/{customerId}")
    @Produces("text/xml")
    @Restrict("#{s:hasRole('admin')}")
    public Customer getCustomer(@PathParam("customerId") int id) {
         return customerDAO.find(id);
    }
}

自然地,面向REST的架构假定客户端正在维护应用状态,因此您的资源组件将是EVENT或APPLICATION作用域的,或者是STATELESS。尽管SESSION作用域是可用的,但默认情况下,会话仅跨越单个HTTP请求,并在HTTP请求后自动销毁。如果要在REST客户端和服务器之间传输会话标识符并利用跨请求的服务器端SESSION作用域,请参阅参考文档中更详细的解释。我们已经有一些关于CONVERSATION作用域集成的想法,更多信息请参考此设计文档

当然,您的资源Seam组件不必是POJO,您还可以使用@Stateless将其转换为EJB。这里的另一个优点是,您无需在components.xmlweb.xml中列出该EJB,因为所有Seam组件都会根据其类型自动找到并注册。

The@Restrict注解只是常规的Seam授权检查,目前您可以为任何其他Seam应用配置BasicDigest身份验证。

CRUD框架集成

Seam有一个快速构建基本CRUD数据库应用的框架,您可能已经在其他Seam示例中看到了EntityHomeEntityQuery。Jozef Hartinger构建了一个扩展,允许您在几分钟内创建一个具有完整HTTP/REST支持的CRUD应用。您可以通过components.xml:

<framework:entity-home name="customerHome"
                       entity-class="my.Customer"
                       auto-create="true"/>

<framework:entity-query name="customerQuery"
		        ejbql="select c from Customer c" order="lastname"/>

<resteasy:resource-home path="/customer" name="resourceCustomerHome"
                        entity-home="#{customerHome}" entity-id-class="java.lang.Long"
                        media-types="application/xml application/json"/>

<resteasy:resource-query path="/customer" name="resourceCustomerQuery"
                         entity-query="#{customerQuery}" entity-class="my.Customer"
                         media-types="application/xml application/json"/>

来声明它。您只需创建实体,然后就可以通过HTTP读取和写入数据库。my.CustomerA

  • GET请求到/customer?start=30&show=10将执行resourceCustomerQuery组件,并返回所有客户列表,带有分页,从第30行开始,结果中有10行。您可以通过
  • PUT请求到, 、和DELETE特定的客户实例,只需发送到/customer的HTTP请求即可。/customer/<customerId>.
  • 发送一个POST/customer?start=30&show=10/customer请求将创建一个新的客户实体实例并持久化它。

请注意,<framework:...>映射是Seam CRUD框架的常规部分,具有所有常规选项,例如查询定制。内容将由内置的RESTEasy提供程序(例如)进行转换,例如XML转换将使用您的实体类上的任何JAXB绑定。

您不必使用XML配置;与Seam CRUD超类ResourceHomeResourceQuery一样,您可以编写子类并使用注解配置映射。

目前这个CRUD框架功能没有在当前版本中进行文档记录的原因:我们不确定API是否会保持不变。请将这个版本视为我们的提议,我们非常需要关于它的反馈,哪些功能有效,哪些可以改进。Jozef还编写了一个具有基于jQuery客户端的完整功能的RESTful应用程序,用于演示CRUD框架。请查看Seam发行版中的任务示例。您可以在Restbay示例中找到更多演示代码和测试,我们使用该示例进行通用的RESTEasy集成测试和演示。

功能简表

我仅突出了Seam和RESTEasy的三个主要功能,但还有更多功能和即将推出。

JAX-RS应用程序中的异常映射到具有名为ExceptionMapper的提供者类的HTTP响应。这可能会比预期的更多工作,因此您还可以在Seam的pages.xml中声明性地映射异常,请参阅文档

您可以编写单元测试,通过Seam和RESTEasy传递模拟的HTTP请求和响应,所有这些都是通过本地调用而不是TCP套接字。我们在集成测试中使用它们,您也可以用来测试您的应用程序。请参阅参考文档

关于《MVC和REST》已有讨论。至少从我的角度来看,这一切都归结于通过链接资源(HATEOAS,超文本作为应用程序状态的引擎)通过超文本驱动应用程序状态。从技术角度来看,这意味着我们需要更多地控制如何呈现视图,而不仅仅是使用JAXB默认值从Customer实体中 marshaling 傻瓜XML文档。我们应该渲染XHTML表示形式,当然这可能包括JAXB渲染的XML块以及链接和表单,并且能够使用模板自定义它们。

Facelets似乎非常适合这个,我们有一个发送模板化XHTML响应的示例

@GET
@Path("/customer/{id}")
@ProduceMime("application/xhtml+xml")
@FaceletsXhtmlResponse(
    template = "/some/path/to/template/#{thisCanEvenBeEL}/foo.xhtml"
)
@Out(value = "currentCustomer", scope = ScopeType.EVENT)
public Customer getCustomer(@PathParam("id") String id) { ... }

这只是一个伪代码,这个功能在发行版中不可用。它本身不会非常有用,因为我们不知道如何将带有XHTML有效负载的传入HTTP请求转换回Facelet视图。这也不容易实现,我们可能会等待JSF2来完成这项工作。但它表明,在同一个应用程序中提供基于JSF的客户端界面和RESTful HTTP Web服务界面可能是自然合适的。

下一个版本?

目前可用的RESTEasy版本仍然不是GA,尽管它是候选版本。还有几个与集成代码相关的开放问题,我们希望关闭它们,并必须最终确定CRUD框架接口。所有这些预计将在Seam 2.2版本中发生。

更详细的功能,如会话集成、表示模板或额外的认证方案可能留给了Seam3,因为我们可能希望尽可能多地基于新的JSF2/JCDI标准进行构建。请关注这个维基页面以获取更新。

附言:这本书是了解这些内容的绝佳起点。

更新:我忘记提到了一个可能大家都会喜欢的重要功能。您可以为Seam组件(POJO或EJB)的接口而不是Bean类进行注解。对于EJB Seam组件,实际上您必须注解本地业务接口。

@Path("/customer")
public interface MyCustomerResource {

    @GET
    @Path("/{customerId}")
    @Produces("text/xml")
    public Customer getCustomer(@PathParam("customerId") int id);
}
@Name("customerResource")
public class MyCustomerResourceBean implements MyCustomerResource {

    @In
    CustomerDAO customerDAO;

    @Restrict("#{s:hasRole('admin')}")
    public Customer getCustomer(int id) {
         return customerDAO.find(id);
    }
}

返回顶部