Java 开发 2.0: 使用 Gretty 的超轻量级 Java Web 服务

Gretty 丢弃 Web 堆栈来实现真正的快速应用程序开发

Gretty 是构建 Web 服务超轻量级框架的新学派之一。构建于极快的 Java™ 之上,Gretty 将 Groovy 用作 Web 端点和 Grape 的 Maven 式依赖关系管理的一种域特定语言。本文介绍如何开始使用 Gretty 来构建和部署 Java Web 服务应用程序。

Andrew Glover, 作家和开发人员

Andrew Glover 是具有行为驱动开发、持续集成和敏捷软件开发激情的开发人员、作家、演说家和企业家。他是 easyb 行为驱动开发(Behavior-Driven Development,BDD)框架的创建者和三本书的合著者:持续集成Groovy 在行动Java 测试模式。您可以通过他的博客与他保持一致并在 Twitter(http://twitter.com/aglover)上关注他。



2011 年 9 月 25 日

纵观 Java 开发 2.0 中最新的几篇文章,我已经构建了一个简单的从云到移动终端的应用程序。该应用程序名为 Magnus,充当监听移动设备位置信息的 HTTP 端点。 它通过接收 HTTP PUT 请求来运行,每个请求包含一个 JSON 文档,指出给定时间内帐户的位置。到目前为止,我已经使用了 Web 框架 Play 来开发和扩展 Magnus(参见 参考资料)。

关于本系列

自 Java 技术首次诞生以来,Java 开发格局已发生了翻天覆地的变化。得益于成熟的开源框架和可靠的租赁部署基础设施,现在可以迅速而经济地组装、测试、运行和维护 Java 应用程序。在本 系列 中,Andrew Glover 探索使这种新的 Java 开发风格成为可能的各种技术和工具。

Play 提供一个 MVC 堆栈,就这点它与 Grails 很相似。使用 Play,您可以很容易地定义利用视图(JSP、GSP、模板等)的控制器(servlets),在某种程度上,控制器管理模型。模型是使用经 Hibernate、JPA 或其他类似 ORM 的好技术增强的 POJO(传统 Java 对象)实现的。

尽管 MVC 是较老的标准,随着 Grail 和 Play 等框架的出现,很多都已经发生了改变。回想一下曾经维护简单的 Web 请求-响应交互所需的工作量(比方说使用 Struts),您会明白我们为快速构建 MVC Web 应用程序而做了多大的改进。当然,并非所有的 Web 应用程序都需要 MVC 基础架构才能工作。如今,一些 Web 应用程序根本就不再需要 MVC “堆栈”。

为了反对这样一种反常的论调,在您关闭浏览器之前,回顾一下 Magnus。虽然为了演示,对 Magnus 进行了严格的设计,我的云到移动终端的应用程序不包含传统的视图组件,主要包含了现有的成功服务的模型。与 Twitter 或 Foursquare 一样,Magnus 接收来自世界各地不同设备的消息。广义上说,Magnus 是一个 Web 服务,而并不是每个 Web 服务都需要 MVC 堆栈框架才能完成工作。在某些情况下,您所需要的是一个超级轻量的 Web 框架,而不是 Web 堆栈。

本月,我们将着眼于以下内容之一:快速开发框架,太新以至于还没有自己的主页,或许并不需要主页。Gretty 的沿袭和隶属成员(分别包括 Netty 和 Groovy)具有足够的名望,它已经是 Java 2.0 Web 开发系列的一部分。它填补了一个许多开发人员仍然不知道他们已经具有的需求(这就是真正的 Web 2.0 风格,您知道吗?)。如果您愿意走狂野的一面,它也可足够稳定地用作生产之用。

快速 Java 开发的历史

老的足以记得何时第一次引入 Servlets API 的我们有理由对新的 “轻量级” 范式持怀疑态度;毕竟仅仅一个简单的 servlet 便让您构建一个 Web 服务,而不需要大量的代码和由此产生的 JAR 文件。Web 服务框架,比如 Restlet 或 Jersey,采取了稍微不同的开发加速方法,以类扩展、注释,甚至标准的 JSR 为基础来创建 RESTful Web 服务。在某些情况下,它们仍然是很好的选择。

但事实证明,一些新的轻量级(相对于旧的 轻量级)框架使得 Web 服务或简单的 HTTP 端点(也称为路由)极其易于定义。甚至比手动塞入一个 servlet 还要简单!

这些框架首次出现在其他平台上,尤其是用于 Ruby 的 Sinatra 和用于 Node.js 的 Express。但是针对 Java 平台的有趣项目也已经开始出现了。Gretty 就是其中之一,当然 Gretty 是为 Groovy 和 JVM 产生的。


我和 Gretty

就我而言,Gretty 至少有两点符合:首先是使用 Groovy 的 Grape(我不久将会详细地对其进行描述)以方便依赖性管理。其次是其简单的用于定义端点的 DSL 式的语法。 使用 Gretty,您可以非常快地(只用短短的几行代码)定义和部署一个工作的 Web 运行框架,该框架处理实际的业务逻辑。作为示例,请看我快速地写出清单 1 中的典型 hello world 示例:

清单 1. Hello, World:这就是 Gretty!
import org.mbte.gretty.httpserver.* 

@GrabResolver(name='gretty', 
  root='http://groovypp.artifactoryonline.com/groovypp/libs-releases-local')
@Grab('org.mbte.groovypp:gretty:0.4.279') 

GrettyServer server = [] 
server.groovy = [ 
    localAddress: new InetSocketAddress("localhost", 8080), 
    defaultHandler: { 
        response.redirect "/" 
    }, 
    "/:name": {
        get {
            response.text = "Hello ${request.parameters['name']}"
        } 
    } 
] 
server.start()

清单 1 中,我创建了一个服务器监听端口 8080,然后设置一个包含参数 name 的简单 root 端点。到其他端点的任何请求都将通过 defaultHandler 返回到 /。简单地说,使用 / 的位置,处理程序为请求的客户端发送一个 HTTP 301 “moved permanently” 代码 。所有请求会收到一个包含字符串 “Hello” 和任何已传递参数值的响应(将 content-type 设置为 text/plain);例如,/Andy 将会生成 “Hello Andy”。

Gretty 和 NIO

Gretty 是使用 Netty 构建的,它是一个大量使用 Java NIO 库的客户端-服务器框架。NIO 或新 I/O、I/O 库包在 Java 1.4 出现时就已经被引入。 NIO 主要因为其非阻塞的 IO 操作获得关注,这就允许了可伸缩的服务器构造。这对 Netty 和 Gretty 等框架是很有用的。

那么 清单 1 中最有趣的是什么?首先,您在清单中所看到的都是您的应用程序所需要的。没有配置文件。不需要直接下载或安装任何东西(除了 Groovy 1.8)。要激活该示例,只要输入 groovy server.groovy

现在如果您的响应要求更复杂的文本而不是简单文本,该怎么办?对于该问题,Gretty 有很多选择,其中有两个是十分简单的。第一个是您可以简单地将响应类型设置为 HTML,正如我在清单 2 中所执行的:

清单 2. Gretty 中的 HTML 响应
"/:name": {
 get {
  response.html = "Hello ${request.parameters['name']}"
 } 
}

在这种情况下,响应的 content-type 会被设置为 text/html。另外,Gretty 可以利用静态和动态的模板。例如,我可以使用一个类似于 JSP/GSP 的简单构造函数来定义模板,类似于清单 3:

清单 3. Gretty 中的 HTML 模板
<html>
 <head>
  <title>Hello!</title>
 </head>
 <body>
  <p>${message}</p>
 </body>
</html>

然后可以在某个响应的主体部分引用该模板:

清单 4. Gretty 中的 Groovy++ 模板
"/:name" {
  get {
   response.html = template("index.gpptl", 
     [message: "Hello ${request.parameters['name']}"])
  }
}

Getty 和 Grape 的依赖性管理

Gretty 令人难忘的开发速度都归功于 Grape(参见 参考资料),Gretty 使用 Grape 来自动下载二进制文件的依赖关系或 JAR 文件。使用 Maven 的传递依赖来加载所有文件。在 清单 1 中我所需要做的就是输入注释 @Grab('org.mbte.groovypp:gretty:0.4.279'),然后我会获得与 Gretty 相关联的 JAR 文件,以及 Gretty 的依赖项。注释 @GrabResolver(name='gretty', root='http://groovypp.artifactoryonline.com/groovypp/libs-releases-local') 表示 Grape 在何处可以发现所需的文件。

Gretty 不仅仅是 Groovy

Gretty 并不限制于 Groovy 语言。与在 JVM 上运行的所有语言一样,您可以尝试使用 Java 语言或者甚至 Scala 语言来编写 Gretty 应用程序。

Grape 可能看起来简单,但是并不意味着它不适合生产。事实上,Grape 对所需依赖项的自动下载与 Maven 没有任何不同。只是 Grape 在运行时(首次运行应用程序时)下载,而 Maven 在构建时下载所需的依赖项。如果 Grape 可以在本地找到所需的依赖项,则不需要再进行下载。所需的 JAR 文件被自动放置在应用程序的类路径中。因此,您只需要为首次 运行某个已配置的 Grape 应用程序支付性能成本。当然,也会在您更改指定依赖项的所需版本时受到一个小的性能影响。


Gretty 适合 Magnus

希望到目前为止您已经发现 Gretty 是简单的,这就更易于进行非常快速的开发。此外,Gretty(或与此类似的框架)特别适合于 Magnus(HTTP 端点数据监听)这样的应用程序。那么让我们看看当完全使用一个 Gretty 编写的更轻量的应用程序来替换一个相对轻量的框架(比如 Play 或 Grails)时将会发生什么。

对于 Magnus 的具体实现,我将使用 Morphia 和 MongoHQ,您可以回顾我的 Amazon Elastic Beanstalk 简介。为了利用具有新配置的 Groovy 的 Grape 实用工具,我需要将清单 5 中的注释添加到服务器中:

清单 5. 添加 Morphia 及其依赖项
@GrabResolver(name='morphia', root='http://morphia.googlecode.com/svn/mavenrepo/')
@Grab(group='com.google.code.morphia', artifactId='morphia', module="morphia", 
  version='0.99')

我的 Morphia 类与 Magnus 的早期具体实现中的一样:我有一个 Account 和一个 Location。在此端点中,我只简单地更新了某个给定帐户的位置。因为 Morphia 的客户端会将 JSON 文档发送至 Gretty 端点,我还要使用 Jackson(一个非常好的处理 JSON 的框架),它已经是 Gretty 的一部分。得益于 Grape 传递依赖的处理,现在我可以访问用于解析传入的 JSON 文档并将其转换为一个简单的 Java Map 所需要的一切。

清单 6. 在 Gretty 中更新位置
def server = new GrettyServer().localAddress(new InetSocketAddress("localhost", 8080)).
 "/location/:account" {
  put {
    def jacksonMapper = new ObjectMapper()
    def json = jacksonMapper.readValue(request.contentText, Map.class)
    def formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm")
    def dt = formatter.parse(json['timestamp'])
    def res = [:]
    try{
      new Location(request.parameters['account'], dt, json['latitude'].doubleValue() , 
          json['longitude'].doubleValue() ).save()
	  res['status'] = 'success'
    }catch(exp){
      res['status'] = "error ${exp.message}"
    }
    response.json = jacksonMapper.writeValueAsString(res)
  }
}
server.start ()

正如您在 清单 6 中所看到的,创建了一个传入 JSON 文档的 Map(称为 json),然后通过清单 7 中的 Location 类相应地将其插入 MongoDB:

清单 7. 创建位置文档 Gretty redux
import com.google.code.morphia.annotations.Entity

@Entity(value = "locations", noClassnameStored = true)
class Location extends AbstractModel {
 String accountId
 double latitude
 double longitude
 Date timestamp

 public Location(String accountId, Date timestamp, double lat, double lon) {
  this.accountId = accountId
  this.timestamp = timestamp
  this.latitude = lat
  this.longitude = lon
 }
}

另外,Location 有一个 Groovy 超类,如清单 8 所示:

清单 8. Location 的基类
import com.google.code.morphia.Morphia
import com.google.code.morphia.annotations.Id
import com.mongodb.Mongo
import org.bson.types.ObjectId

abstract class AbstractModel {
  @Id
  private ObjectId id;

  def save() throws Exception {
    def mongo = new Mongo("fame.mongohq.com", 32422)
    def datastore = new Morphia().createDatastore(mongo, "xxxx", 
      "xxxx", "xxxx".toCharArray())
    datastore.save(this)
    return this.id
  }
}

您可能记得出自 “Climb the Elastic Beanstalk” 中清单 3 的代码。为了 Gretty 的实现,我所做的惟一更改是将实际的文件名从 Location.java 改为 Location.groovy,这意味着在激活服务器之前我不需要对其进行编译。我还添加了一个基类。通过从 URI 获得的传入参数 account 将位置与某个帐户相关联。

然后用 JSON 发送一个表示成功的响应。如果有错误,会产生另一个响应。


结束语:Gretty 已就绪

Gretty 是极其轻量级的。没有嵌入的 ORM 框架。除了简单的模板之外,没有强大的视图框架,但是插入一些其他的框架是完全可行的。所有这些是否意味着 Gretty 不适合日常使用?缺少测试框架是否也有同样的意思?答案是否定的:首先,Gretty 构建于 Netty 深受认可的代码之上,所以您大可放心。其次,您可以对 Gretty 进行自动或非自动的测试,就像您对任何其他 Web 端点所进行的测试一样。事实上,如果您想要了解 Gretty 是如何进行测试,请查看 Gretty 的源代码。Gretty 源代码中有大量的测试!

Gretty 与现代的全堆栈 Web 框架相对立,正是因为有时您不需要整个堆栈。如果您发现使用像 Gretty 这样的框架做了太多的工作,那么您可能最好使用许多全堆栈、存档完好的 Java Web 框架中的一个。同样地,如果您想要知道为什么需要整个堆栈来处理 Web 服务请求和响应,那么 Gretty 可能正是您所需要的。

参考资料

学习

获得产品和技术

讨论

  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Cloud computing, Web development, Open source
ArticleID=761000
ArticleTitle=Java 开发 2.0: 使用 Gretty 的超轻量级 Java Web 服务
publish-date=09252011