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

smi*_*jka 1 java rest web-services jax-rs jersey-2.0

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

我读到:

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

我的问题是,当您说:“球衣负责在定义的 'PRODUCES' responseType 中编组这些对象并将其写入客户端流中”,您所说的球衣是什么意思,处理对象的实际类或库是什么。

当我读到 jersey 是处理 JAX-RS API 规范的引擎时,我感到很困惑。有人可以解释一下这句话中 jersey 一词背后的确切含义吗?泽西岛的哪个实际类在泽西岛完成处理请求和响应的工作?

Pau*_*tha 6

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

假设我想要一个解析库。我知道我想如何使用它。唯一的问题是我不太擅长编写解析代码。所以我创建了一个高级规范,并将实现外包。以下是规范中的三个类。它们都包含在一个“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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在 Stack Overflow 给了我一个带有这三个类和所需 META-INF/services 文件(根据 ServiceLoader 模式)的 jar(比如so-myparsers-impl.jar),现在当我添加 so-myparsers-impl 时。 jar 到我的项目并尝试再次运行它,该程序现在可以工作,因为现在它有一个implementation

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

您检查书面规范规范定义,您会注意到源代码中包含的唯一类是规范中提到的类。但是您应该注意的是,书面规范根本没有提及它应该如何实现。以下面的代码为例

@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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

那么,Java 中是否有一些我们不知道的魔法伏都教,它会使这个Application类启动一个服务器,还有一些使带@Path注释的类自动接受请求的骗局。不。某些机构需要提供引擎。引擎可能有 20,000 行代码,只是为了使上述代码按规定工作。

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

至于Jersey如何实现引擎,那太宽泛了。我能做的就是让你对幕后发生的事情有一个高层次的概述。

JAX-RS 应用程序被定义为在 servlet 容器内运行。如果您了解 servlet 容器和 servlet 规范,那么您就会知道处理请求的唯一方法是编写 aHttpServletFilter. 因此,如果您想实现 JAX-RS,那么您需要能够通过 aHttpServlet或 a处理请求Filter。在ServletContainer你提到的,实际上是两者兼而有之。因此,对于 Jersey,就请求处理而言,这是 Jersey 应用程序的“入口点”。它可以通过多种方式进行配置(我已将这项研究留给您)。

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

如果你真的想深入了解幕后发生的事情的血腥细节,你只需要深入研究源代码,从入口点开始,ServletContainer. 准备好花几个月的时间来真正了解这一切是如何运作的。如果这是您所期望的,这不是在 Stack Overflow 上的一篇帖子中可以解释的。