JAX-RS is just an API! – JAX-RS只是一个API!

最后修改: 2017年 1月 18日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

The REST paradigm has been around for quite a few years now and it’s still getting a lot of attention.

REST范式已经存在了好几年了,它仍然得到了很多关注。

A RESTful API can be implemented in Java in a number of ways: you can use Spring, JAX-RS, or you might just write your own bare servlets if you’re good and brave enough. All you need is the ability to expose HTTP methods – the rest is all about how you organize them and how you guide the client when making calls to your API.

RESTful API可以通过多种方式在Java中实现:你可以使用Spring、JAX-RS,或者如果你足够优秀和勇敢,你也可以自己编写裸露的Servlets。你所需要的只是暴露HTTP方法的能力–剩下的就是你如何组织这些方法,以及你在调用API时如何引导客户端。

As you can make out from the title, this article will cover JAX-RS. But what does “just an API” mean? It means that the focus here is on clarifying the confusion between JAX-RS and its implementations and on offering an example of what a proper JAX-RS webapp looks like.

从标题可以看出,这篇文章将涉及JAX-RS。但 “只是一个API “是什么意思呢?它意味着这里的重点是澄清JAX-RS和它的实现之间的混淆,以及提供一个适当的JAX-RS网络应用的例子。

2. Inclusion in Java EE

2.包含在Java EE中

JAX-RS is nothing more than a specification, a set of interfaces and annotations offered by Java EE. And then, of course, we have the implementations; some of the more well known are RESTEasy and Jersey.

JAX-RS只不过是一种规范,是由Java EE提供的一组接口和注解。当然,我们还有一些实现;其中一些比较知名的是RESTEasyJersey

Also, if you ever decide to build a JEE-compliant application server, the guys from Oracle will tell you that, among many other things, your server should provide a JAX-RS implementation for the deployed apps to use. That’s why it’s called Java Enterprise Edition Platform.

另外,如果你决定建立一个符合JEE标准的应用服务器,来自Oracle的人会告诉你,在许多其他方面,你的服务器应该提供一个JAX-RS实现,供部署的应用程序使用。这就是为什么它被称为Java企业版Platform

Another good example of specification and implementation is JPA and Hibernate.

另一个规范和实现的好例子是JPAHibernate

2.1. Lightweight Wars

2.1.轻量级战争

So how does all this help us, the developers? The help is in that our deployables can and should be very thin, letting the application server provide the needed libraries. This applies when developing a RESTful API as well: the final artifact should not contain any information about the used JAX-RS implementation.

那么,所有这些对我们这些开发者有什么帮助呢?帮助在于,我们的部署品可以而且应该非常薄,让应用服务器提供所需的库。这一点在开发RESTful API时也适用:最终的工件不应包含任何关于所使用的JAX-RS实现的信息。

Sure, we can provide the implementation (here‘s a tutorial for RESTeasy). But then we cannot call our application “Java EE app” anymore. If tomorrow someone comes and says “Ok, time to switch to Glassfish or Payara, JBoss became too expensive!“, we might be able to do it, but it won’t be an easy job.

当然,我们可以提供实现(这里是一个RESTeasy的教程)。但这样我们就不能再把我们的应用程序称为 “Java EE应用程序”。如果明天有人过来说:”Ok,是时候换成Glassfish或Payara了,JBoss变得太贵了!“,我们也许能做到,但这并不是一件容易的事。

If we provide our own implementation we have to make sure the server knows to exclude its own – this usually happens by having a proprietary XML file inside the deployable. Needless to say, such a file should contain all sorts of tags and instructions that nobody knows nothing about, except the developers who left the company three years ago.

如果我们提供我们自己的实现,我们必须确保服务器知道排除它自己的实现–这通常是通过在可部署的文件里有一个专有的XML文件来实现的。不用说,这样的文件应该包含各种标签和指令,除了三年前离开公司的开发人员之外,没有人知道这些。

2.2. Always Know Your Server

2.2.始终了解你的服务器

We said so far that we should take advantage of the platform that we’re offered.

我们说到目前为止,我们应该利用好提供给我们的平台。

Before deciding on a server to use, we should see what JAX-RS implementation (name, vendor, version and known bugs) it provides, at least for Production environments. For instance, Glassfish comes with Jersey, while Wildfly or Jboss come with RESTEasy.

在决定使用哪个服务器之前,我们应该看看它所提供的JAX-RS实现(名称、供应商、版本和已知的bug),至少对于生产环境来说。例如,Glassfish提供了Jersey,而Wildfly或Jboss则提供了RESTEasy。

This, of course, means a little time spent on research, but it’s supposed to be done only once, at the beginning of the project or when migrating it to another server.

当然,这意味着要花一点时间进行研究,但应该只做一次,在项目开始时或将其迁移到另一个服务器时。

3. An Example

3.一个例子

If you want to start playing with JAX-RS, the shortest path is: have a Maven webapp project with the following dependency in the pom.xml:

如果你想开始玩JAX-RS,最短的路径是:拥有一个Maven webapp项目,其pom.xml中的依赖关系如下。

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-api</artifactId>
    <version>7.0</version>
    <scope>provided</scope>
</dependency>

We’re using JavaEE 7 since there are already plenty of application servers implementing it. That API jar contains the annotations that you need to use, located in package javax.ws.rs. Why is the scope “provided”? Because this jar doesn’t need to be in the final build either – we need it at compile time and it is provided by the server for the run time.

我们使用JavaEE 7,因为已经有很多应用服务器在实现它。那个API jar包含了你需要使用的注解,位于包javax.ws.rs中。为什么范围是 “提供”?因为这个jar也不需要在最终的构建中出现–我们在编译时需要它,它是由服务器为运行时提供的。

After the dependency is added, we first have to write the entry class: an empty class which extends javax.ws.rs.core.Application and is annotated with javax.ws.rs.ApplicationPath:

在添加了依赖关系之后,我们首先要编写入口类:一个空的类,它扩展了javax.ws.rs.core.Application,并以javax.ws.rs.ApplicationPath>为注释。

@ApplicationPath("/api")
public class RestApplication extends Application {
}

We defined the entry path as being /api. Whatever other paths we declare for our resources, they will be prefixed with /api.

我们将入口路径定义为/api。无论我们为资源声明什么其他路径,它们都将以/api为前缀。

Next, let’s see a resource:

接下来,让我们看看一个资源。

@Path("/notifications")
public class NotificationsResource {
    @GET
    @Path("/ping")
    public Response ping() {
        return Response.ok().entity("Service online").build();
    }

    @GET
    @Path("/get/{id}")
    @Produces(MediaType.APPLICATION_JSON)
    public Response getNotification(@PathParam("id") int id) {
        return Response.ok()
          .entity(new Notification(id, "john", "test notification"))
          .build();
    }

    @POST
    @Path("/post/")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response postNotification(Notification notification) {
        return Response.status(201).entity(notification).build();
    }
}

We have a simple ping endpoint to call and check if our app is running, a GET and a POST for a Notification (this is just a POJO with attributes plus getters and setters).

我们有一个简单的ping端点来调用并检查我们的应用程序是否正在运行,还有一个GET和POST的Notification(这只是一个带有属性和getters和setters的POJO)。

Deploy this war on any application server implementing JEE7 and the following commands will work:

在任何实施JEE7的应用服务器上部署这个战争,下面的命令就能发挥作用。

curl http://localhost:8080/simple-jaxrs-ex/api/notifications/ping/

curl http://localhost:8080/simple-jaxrs-ex/api/notifications/get/1

curl -X POST -d '{"id":23,"text":"lorem ipsum","username":"johana"}' 
  http://localhost:8080/simple-jaxrs-ex/api/notifications/post/ 
  --header "Content-Type:application/json"

Where simple-jaxrs-ex is the context-root of the webapp.

其中simple-jaxrs-ex是webapp的context-root。

This was tested with Glassfish 4.1.0 and Wildfly 9.0.1.Final. Please note that the last two commands won’t work with Glassfish 4.1.1, because of this bug. It is apparently a known issue in this Glassfish version, regarding the serialization of JSON (if you have to use this server version, you’ll have to manage JSON marshaling on your own)

这是用Glassfish 4.1.0和Wildfly 9.0.1.Final测试的。请注意,由于这个错误,最后两个命令在Glassfish 4.1.1中无法工作。这显然是这个Glassfish版本中的一个已知问题,关于JSON的序列化(如果你必须使用这个服务器版本,你将不得不自己管理JSON marshaling)

4. Conclusion

4.结论

At the end of this article, just keep in mind that JAX-RS is a powerful API and most (if not all) of the stuff that you need is already implemented by your web server. No need to turn your deployable into an unmanageable pile of libraries.

在这篇文章的最后,请记住JAX-RS是一个强大的API,你所需要的大部分(如果不是全部)东西已经由你的Web服务器实现了。没有必要把你的可部署性变成一堆难以管理的库。

This write-up presents a simple example and things might get more complicated. For instance, you might want to write your own marshalers. When that’s needed, look for tutorials that solve your problem with JAX-RS, not with Jersey, Resteasy or other concrete implementation. It’s very likely that your problem can be solved with one or two annotations.

这篇文章介绍了一个简单的例子,事情可能会变得更加复杂。例如,你可能想写你自己的marshalers。当需要这样做的时候,请寻找用JAX-RS来解决你的问题的教程,而不是用Jersey、Resteasy或其他具体的实现。你的问题很有可能用一两个注解就能解决了。