Jersey 框架如何在 REST 中实现 JAX-RS API?

How is Jersey framework implement JAX-RS API in REST?

我知道这个问题有很多答案,但我仍然对 JAX-RS API(规范)和 Jersey 框架(参考实现)之间的区别感到困惑。

我读到:

Jersey 框架基本上使用com.sun.jersey.spi.container.servlet.ServletContainer servlet 来拦截所有传入的请求。正如我们在项目 web.xml 中配置的那样,所有传入的其余请求都应由该 servlet 处理。有一个配置了 jersey servlet 的 init-param 来查找您的 REST 服务 classes。 REST 服务 classes 不是 Servlet,它们不需要像您在代码中那样扩展 HttpServlet。这些 REST 服务 class 是简单的 POJO 注释,用于告诉 jersey 框架不同的属性,例如路径、消费、生产等。当您从您的服务方法 return 时,jersey 负责将这些对象编组到定义的 'PRODUCES' responseType 并将其写入客户端流

我的问题是当你说:“jersey 负责编组定义的 'PRODUCES' responseType 中的那些对象并将其写入客户端流”,你所说的 jersey 是什么意思,实际是什么 class 或处理对象的库 .

当我读到 jersey 是处理 JAX-RS API 规范的引擎时,我很困惑。有人可以解释一下这句话中球衣这个词背后到底是什么吗?泽西岛的实际 class 在泽西岛处理请求和响应的工作是什么?

您已经指出 JAX-RS 是一个 规范 而 Jersey 是 实现 这就是 Java 特别是Java EE 工作,也许这个 article 可以解释得更好。

总而言之,JAX-RS 只是一个规范,没有真正的实现。真正的实现是由 Jersey 和其他遵循 JAX-RS 规范的库完成的。

规范和实现的概念确实是非常基本的软件工程概念。您的规格是高级设计。为了帮助理解,我只是想出了一个非常简单的例子。

假设我想要一个解析库。我知道我希望如何使用它。唯一的问题是我不太擅长编写解析代码。所以我创建了一个高级规范,并将实施外包出去。这是规范中的三个 classes。它们都包含在一个 "API jar" 中,比如 myparsers-api.jar

public interface Parser {
    String[] parse(String s);
}

public interface ParserFactory {
    Parser getBySpaceParser();
    Parser getByCommaParser();
}

public class ParserDepot {
    private static ServiceLoader<ParserFactory> loader
            = ServiceLoader.load(ParserFactory.class);

    public static ParserFactory getDefaultParserFactory() {
        final List<ParserFactory> factories = new ArrayList<>();
        loader.forEach(factories::add);
        if (factories.isEmpty()) {
            throw new IllegalStateException("No ParserFactory found");
        }
        return factories.get(0);
    }
}

所以在这一点上,我实际上可以针对这个 jar 编码。如果我现在在另一个项目中使用它,该项目将编译得很好。

ParserFactory factory = ParserDepot.getDefaultParserFactory();
Parser parser = factory.getBySpaceParser();
String[] tokens = parser.parse("Hello World");
System.out.println(Arrays.toString(tokens));

所以即使没有实现这个规范,我仍然可以针对它进行编码,并针对它进行编译。但是当我尝试实际 运行 程序时,它不会工作,因为没有实现。您可以尝试 运行 这段代码,您将得到一个 IllegalStateException(如果您不熟悉此模式,请参阅 the docs for ServiceLoader)。

所以我将实施外包给一家名为 Stack Overflow 的公司。他们得到了我的 myparsers-api.jar,他们需要给我一个实现。他们需要实施一个 ParserFactory 和几个 Parser。它们可能看起来像这样

public class SoByCommaParser implements Parser {
    @Override
    public String[] parse(String s) {
        return s.split("\s+,\s+");
    }
}

public class SoBySpaceParser implements Parser {
    @Override
    public String[] parse(String s) {
        return s.split("\s+");
    }
}

public class SoParserFactory implements ParserFactory {
    @Override
    public Parser getBySpaceParser() {
        return new SoBySpaceParser();
    }

    @Override
    public Parser getByCommaParser() {
        return new SoByCommaParser();
    }
}

现在 Stack Overflow 给了我一个 jar(比如 so-myparsers-impl.jar),其中包含这三个 classes 和所需的 META-INF/services 文件(根据 ServiceLoader 模式),现在当我将 so-myparsers-impl.jar 添加到我的项目并再次尝试 运行 时,程序现在可以运行了,因为现在它有一个 实施.

这正是 JAX-RS 规范的工作原理。它只定义了它应该如何工作的高级设计。作为该设计一部分的 classes、接口和注释被放置在 "API jar" 中,就像我的高级解析器被放入 jar 中一样。实现不能改变这些 classes。所有属于 JAX-RS 规范(版本 2.x)的 classes 都被放入一个 jar javax.ws.rs-api 中。您可以针对该 jar 进行编码,您的代码将编译得很好。但是没有什么可以做的"work".

您检查了两个 the written specification and the classes defined by the specification,您会注意到源代码中包含的唯一 class 是规范中提到的那些。但是您应该注意的是,书面规范根本没有提及它应该如何实现。以下面的代码为例

@Path("/test")
public class TestResource {
    @GET
    public String get() {
        return "Testing";
    }
}

@ApplicationPath("/api")
public class MyApplication extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        Set<Class<?>> classes = new HashSet<>();
        classes.add(TestResource.class);
        return classes;
    }
}

现在规范指出,这就是我们在 servlet 容器中 运行 JAX-RS 应用程序所需要的全部。这就是它所说的全部。它没有说明这一切应该如何运作。这就是设计的工作方式。

那又怎样,Java 中是否有一些我们不知道的魔法巫术会让这个 Application class 启动服务器,以及一些魔术使 @Path 注释 class 自动接受请求。不,有些机构需要提供 engine。引擎可能有 20,000 行代码只是为了让上面的代码按规定工作。

也就是说,Jersey 只是一个实现的名称。就像我将解析器实现外包给 Stack Overflow 一样; Jersey这个名字本身就是项目的名字,就像Hadoop是项目的名字一样。在这种情况下,项目是 JAX-RS 规范的实现。并且由于 JAX-RS 只是一个规范,这意味着 任何人 都可以实现它。如果愿意,您可以编写自己的实现。只要它按照书面规范中定义的方式工作,那么您就可以说您的代码是 JAX-RS 的实现。那里不仅仅是泽西岛;你还有 RESTEasy,这是另一个实现。

如何 Jersey 实现引擎而言,范围太广了。我能做的,就是让你对幕后发生的事情有一个高层次的概述。

JAX-RS 应用程序被定义为 运行 在 servlet 容器内。如果您了解 servlet 容器和 servlet 规范,那么您就会知道处理请求的唯一方法是编写 HttpServletFilter。因此,如果您想实现 JAX-RS,那么您需要能够通过 HttpServletFilter 处理请求。你说的ServletContainer,其实两者都是。所以对于 Jersey,就请求处理而言,这是 Jersey 应用程序中的 "entry point"。它可以通过多种方式进行配置(我已将研究留给您)。

如果您了解如何编写自己的 servlet,那么您就会知道您得到的只是 HttpServletRequestHttpServletResponse。您需要从那里弄清楚该怎么做;从请求中获取请求信息,并在响应中发回响应信息。泽西岛处理所有这些。

如果您真的想了解幕后发生的血淋淋的细节,您只需要从入口点 ServletContainer 开始深入研究源代码。准备好花几个月的时间来真正了解它是如何工作的。这不是一个 Stack Overflow 可以解释的东西 post,如果这是你所期待的。