使用 web.xml、Servlet 3.0 和 Jersey 部署 JAX-RS 应用程序

Deployment of a JAX-RS application using web.xml, Servlet 3.0 and Jersey

我正在尝试使用 web.xml、servlet 3.0 和 jersey API 部署我的应用程序。不幸的是,它不起作用。

这是 MyApplication.class :

1
2
3
4
5
6
7
8
9
package com.example;

public class MyApplication extends Application {
   public Set<Class< ? >> getClasses() {
       Set<Class< ? >> s = new HashSet<Class< ? >>();
       s.add(MyResource.class);
       return s;
   }
}

这是我的资源:

1
2
3
4
5
6
7
8
@Path("/helloworld")
@Produces(MediaType.TEXT_PLAIN)
public class MyResource {
    @GET
    public String getHello() {
        return"HelloWorld !";
    }
}

还有我的 web.xml :

1
2
3
4
5
6
7
8
9
 <web-app>
     <servlet>
         <servlet-name>com.example.MyApplication</servlet-name>
     </servlet>
     <servlet-mapping>
         <servlet-name>com.example.MyApplication</servlet-name>
         <url-pattern>/webapi/*</url-pattern>
     </servlet-mapping>
 </web-app>

在客户端,我正在使用这个网址:
http://localhost:8080/[项目名称]/webapi/helloworld

我有这个错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java.lang.NullPointerException
    sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
    java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1629)
    org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
    org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:491)
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1023)
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    org.apache.tomcat.util.net.AprEndpoint$SocketProcessor.run(AprEndpoint.java:1852)
    java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    java.lang.Thread.run(Thread.java:662)

怎么了? :/ 我正在使用 Tomcat 7.

PS:使用 servlet 2.x,它可以工作:

1
2
3
4
5
6
7
8
9
10
11
12
    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.example</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/webapi/*</url-pattern>
    </servlet-mapping>

但我稍后需要异步模式。

谢谢!


更新:自从写了这个答案,我已经找到了一种方法来避免使用官方 Glassfish Jersey 实现在 Tomcat 上需要 web.xml。详情请看这里。

如果您正在使用标准的 Tomcat 安装(或其他一些 servlet 容器),AFAIK 您无法避免明确告诉它要在 web.xml 文件中启动哪些 servlet*。因为无论如何你都必须使用 web.xml ,所以让 restful web 服务正常工作的最简单方法是完全忘记扩展 javax.ws.rs.core.Application 并只在那里指定上下文路径。您仍然可以使用标准 jax-rs 注释来声明实际的 Web 服务。

web.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<web-app
  xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
  version="3.0"
>
  <servlet>
    <servlet-name>rest-test</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.domain.mypackage</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name> rest-test</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

两个值得注意的点:

  • 您需要在 WAR 文件中捆绑一个 REST 实现,因为 servlet 容器通常不包含一个。由于 Jersey 是 JAX-RS 的参考实现,这就是我在上面的 servlet-class 元素中使用的那个。如果需要,您可以将其替换为 Apache CXF 实现。

  • init-param 元素告诉Jersey 你的哪些包要搜索带有Web 服务注释的Java 文件。编辑它以指向您的 Web 服务。请注意,如果您选择使用 apache CXF 而不是 Jersey,则任何 init-param 元素中所需的内容都会有所不同。知道 CXF 的人请发布他们会是什么。

  • 如果您使用的是 Maven,只需在 pom.xml 文件的 dependencies 部分中添加对 jersey-servlet 的依赖项:

    1
    2
    3
    4
    5
    6
    7
    8
    <dependencies>
      <dependency>
        <groupId>com.sun.jersey</groupId>
        jersey-servlet</artifactId>
        <version>1.18.2</version>
      </dependency>
      ...
    </dependencies>

    在此之后,可以直接在 Java 类中使用标准 JAX-RS 注释来声明 Web 服务:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.domain.mypackage;
    import javax.ws.rs.Consumes;
    import javax.ws.rs.Produces;
    import javax.ws.rs.GET;
    import javax.ws.rs.MatrixParam;
    import javax.ws.rs.Path;

    // It's good practice to include a version number in the path so you can have
    // multiple versions deployed at once. That way consumers don't need to upgrade
    // right away if things are working for them.
    @Path("calc/1.0")
    public class CalculatorV1_0 {
      @GET
      @Consumes("text/plain")
      @Produces("text/plain")
      @Path("addTwoNumbers")
      public String add(@MatrixParam("firstNumber") int n1, @MatrixParam("secondNumber") int n2) {
        return String.valueOf(n1 + n2);
      }
    }

    这应该就是你所需要的。如果您的 Tomcat 安装在端口 8080 上本地运行,并且您将 WAR 文件部署到上下文 myContext,则转到

    1
    http://localhost:8080/myContext/rest/calc/1.0/addTwoNumbers;firstNumber=2;secondNumber=3

    ...应该会产生预期的结果 (5)。

    干杯!

    * 如果您知道不使用 web.xml 将 Jersey servlet 添加到 Tomcat 的上下文中的方法,请纠正我——也许通过使用上下文或生命周期侦听器?


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package com.example;

    @ApplicationPath("/webapi")

    public class MyApplication extends Application {

         public Set<Class< ? >> getClasses() {
           Set<Class< ? >> s = new HashSet<Class< ? >>();
           s.add(MyResource.class);
           return s;
       }
    }

    你需要在WEB-INF文件夹下创建web.xml,代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
        <servlet>
            <servlet-name>ServletAdaptor</servlet-name>
            <servlet-class>
                org.glassfish.jersey.servlet.ServletContainer
            </servlet-class>
            <init-param>
                <param-name>jersey.config.server.provider.packages</param-name>
                <param-value>package where MyResource  resides</param-value>
            </init-param>
            <init-param>
                <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
                <param-value>true</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>ServletAdaptor</servlet-name>
            <url-pattern>/webresources/*</url-pattern>
        </servlet-mapping>
        <session-config>
            <session-timeout>
                30
            </session-timeout>
        </session-config>
    </web-app>

    现在您可以删除您的 MyApplication 类。部署和测试 Web 服务。它会工作


    访问此链接可能会有所帮助:
    https://jersey.java.net/documentation/latest/deployment.html#deployment.servlet.2

    部分:4.7.1。 Servlet 2.x 容器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <web-app>
        <servlet>
            <servlet-name>MyApplication</servlet-name>
            <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
            <init-param>
                ...
            </init-param>
        </servlet>
        ...
        <servlet-mapping>
            <servlet-name>MyApplication</servlet-name>
            <url-pattern>/myApp/*</url-pattern>
        </servlet-mapping>
        ...
    </web-app>