如何仅使用注解设置JAX-RS应用程序(无需web.xml)?

83

仅使用注解是否可以设置JAX-RS应用程序?(使用Servlet 3.0和JAX-RS Jersey 1.1.0)

我尝试了,但没有成功。好像需要一些web.xml配置。


配置A(可工作,但需要web.xml配置)

web.xml

   ...
   <servlet>
      <servlet-name>org.foo.rest.MyApplication</servlet-name>
   </servlet>
   <servlet-mapping>
       <servlet-name>org.foo.rest.MyApplication</servlet-name>
       <url-pattern>/*</url-pattern>
   </servlet-mapping>
   ...

Java

@ApplicationPath("/")
public class MyApplication extends Application {
    ...
}

配置 B(不起作用,抛出异常)

@ApplicationPath("/")
@WebServlet("/*") // <-- 
public class MyApplication extends Application {
    ...
}
后者似乎坚持认为应用程序将是Servlet的子类(异常信息不留余地)。
java.lang.ClassCastException: org.foo.rest.MyApplication cannot be cast to javax.servlet.Servlet

问题

  1. 为什么web.xml定义可以工作,但注解不能?它们有什么不同之处?

  2. 是否有一种方法让它起作用,例如使用没有web.xml的JAX-RS应用程序?


1
如果您使用NetBeans,可以使用向导创建RESTFul Web服务。似乎您正在尝试的是向导在版本6.8中执行的操作。我正在使用7.0.1,新方法更简单,但仅使用一个servlet,即com.sun.jersey.spi.container.servlet.ServletContainer,但它在web.xml中定义。 - perissf
6个回答

174

** 如果您使用的是Tomcat或Jetty,请仔细阅读! **

被接受的答案确实有效,但只适用于像Glassfish或Wildfly这样的应用服务器部署的Web应用程序,以及可能带有EE扩展的Servlet容器,例如TomEE。对于标准的Servlet容器(如Tomcat),它不起作用,我相信大多数在这里寻找解决方案的人都想使用它。

如果您正在使用标准的Tomcat安装(或其他一些Servlet容器),则需要包含REST实现,因为Tomcat不带有该实现。如果您使用Maven,则将以下内容添加到dependencies部分:

<dependencies>
  <dependency>
    <groupId>org.glassfish.jersey.bundles</groupId>
    <artifactId>jaxrs-ri</artifactId>
    <version>2.13</version>
  </dependency>
  ...
</dependencies>

然后,只需在您的项目中添加一个应用程序配置类。如果除了为rest服务设置上下文路径之外没有任何特殊的配置需求,则该类可以为空。一旦添加了这个类,您就不需要在web.xml中配置任何内容(或者根本不需要):

package com.domain.mypackage;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("rest") // set the path to REST web services
public class ApplicationConfig extends Application {}

在此之后,使用Java类中的标准JAX-RS注释声明您的Web服务非常简单:
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,那么访问...

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

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


6
@JavaDude - 如果 web.xml 文件不存在,你需要配置 Maven 来构建 WAR 文件。在你的 pom.xml 文件中,在 "build > plugins" 下添加以下内容(如果没有的话):<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-war-plugin</artifactId><version>2.3</version><configuration><failOnMissingWebXml>false</failOnMissingWebXml></configuration></plugin> - Alvin Thompson
6
@JavaDude - 重要的部分是 <failOnMissingWebXml>false</fai‌​lOnMissingWebXml> 选项。 - Alvin Thompson
3
@JavaDude - 如果不是这个问题,很可能是您没有创建一个继承Application并在其上添加@ApplicationPath注释的类。该类可以为空,但必须存在。 - Alvin Thompson
3
只需添加 jersey-container-servlet,而无需拉取整个 jaxrs-ri 依赖项。例如,在我的情况下是 'org.glassfish.jersey.core:jersey-server:2.18' + 'org.glassfish.jersey.containers:jersey-container-servlet:2.18' - leveluptor
3
既然这个问题明确标记为“java-ee”,我不明白为什么要提供和赞同一个与jee环境无关的答案。-1 - Jan Galinski
显示剩余17条评论

53

看起来我所需要做的就是这样(适用于Servlet 3.0及以上版本)

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("/*")
public class MyApplication extends Application {
    ...
}

显然不需要web.xml配置(在Tomcat 7上尝试过)


1
不明白这个是怎么工作的,你只能使用注释而没有其他配置吗?我已经尝试过这样做了,但还是不起作用... - Filipe
2
@Filipe:我猜你正在使用Tomcat或其他Servlet容器。实际上,这在类似Tomcat的Servlet容器上不起作用——它只能在应用服务器(如Glassfish或Wildfly,也许是TomEE)中工作。如果你正在使用Tomcat,请看下面我的(晚些的)答案。 - Alvin Thompson
8
这个答案不适用于Tomcat或Jetty!请查看我的答案,以使它们(以及其他)servlet容器正常工作。 - Alvin Thompson
1
根据您的实现,您确实需要一个web.xml,尽管一个空的web.xml就足够了。请参阅http://docs.jboss.org/resteasy/docs/3.0.9.Final/userguide/html_single/index.html#d4e40。 - Benjamin Maurer
这在WebSphere Liberty上可以工作,但在WebSphere 8.x和9.0 Classic上不行。 - Archimedes Trajano
如果您能够包含您的导入内容,对于那些在构建路径上有多个选项的人来说会非常有帮助。谢谢。 - Mark W

15

第二章 JAX-RS: Java™ API for RESTful Web Services 规范描述了在 Servlet 环境中发布 JAX-RS 应用程序的发布过程(规范中的2.3.2 Servlet部分)。

请注意,仅推荐使用 Servlet 3 环境(规范中的2.3.2 Servlet,第6页):

建议实现支持 Servlet 3 框架可插拔机制,以实现容器之间的可移植性,并利用容器提供的类扫描功能。

简而言之,如果您想使用无 web.xml 方法,则可以使用自定义的 javax.ws.rs.core.Application 实现,该实现使用 javax.ws.rs.ApplicationPath 注释注册 RESTful 服务资源。

@ApplicationPath("/rest")

尽管您特别询问了有关Jersey的内容,但您可能也想阅读文章使用JAX-RS和WebSphere 8.5 Liberty Profile实现RESTful服务,其中我描述了WebSphere Liberty Profile的无web.xml发布过程(使用Apache Wink作为JAX-RS的实现)。

1
谢谢!我有另一个问题困扰着我,它与此密切相关。是否有一种方法可以在根目录中使用路径(例如@ApplicationPath("/*")),并仅使用注释来提供JSP服务?(即使使用web.xml配置也无法实现)-问题在这里:https://dev59.com/OWgv5IYBdhLWcg3wDsrZ,由于您似乎非常了解JAX-RS,您能否看一下? :)也许您会发现我错过了什么...谢谢! - Eran Medan
我不确定。直觉告诉我,它应该可以在没有额外配置的情况下工作,因为将资源解析为处理请求的过程是从精确匹配到URL模式的。我会看一下并回复。感谢你鼓励我扩展我的知识! :-) - Jacek Laskowski
我正在使用@ApplicationPath("")将我的REST服务放置在JBoss EAP 7上的根目录(相对于web应用程序上下文根)中。我既没有beans.xml也没有web.xml - Julien Kronegg

7

您需要在pom.xml中设置正确的依赖关系

<dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet</artifactId>
    </dependency>

更多细节请查看:jax-rs的入门示例


如果使用ResourceConfig/Application,我可以删除web.xml吗? - BAE
是的,但您需要在pom.xml中添加以下内容以使用Maven:<failOnMissingWebXml> - ACV
不需要了,我认为自从Servlet规范3.0以后,就不再需要web.xml文件了。但有时使用web.xml会更方便,因为如果您想要更改某些内容,它不需要重新编译。 - ACV
如果你没有使用Maven呢?我的项目现在太大了,无法回退。 - TheRealChx101
@ACV 这就是我上周以来一直在做的事情。我正在使用Netbeans和GlassFish 4进行开发。我的服务器有Tomcat 9。当在GlassFish下部署WAR文件时,我的WAR文件不使用web.xml。我扩展了javax.ws.rs.core.Application并注册了我的REST资源。我尝试了很多教程,但仍然无法解决404问题。已经过去了一个星期。我尝试了Jetty、TJWS、Undertow等,但都没有成功。 - TheRealChx101
显示剩余5条评论

2
之前提到的依赖对我来说不起作用。根据Jersey用户指南:
Jersey提供了两个Servlet模块。第一个模块是Jersey核心Servlet模块,它提供了核心Servlet集成支持,并且在任何Servlet 2.5或更高版本容器中都是必需的:
<dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet-core</artifactId>
</dependency>

为了支持额外的Servlet 3.x部署模式和异步JAX-RS资源编程模型,需要安装一个额外的Jersey模块:
<dependency>
 <groupId>org.glassfish.jersey.containers</groupId>
 <artifactId>jersey-container-servlet</artifactId>
</dependency>

jersey-container-servlet模块依赖于jersey-container-servlet-core模块,因此在使用时,无需显式声明jersey-container-servlet-core依赖项。

https://jersey.github.io/documentation/latest/deployment.html#deployment.servlet.3


0
正如@Eran-Medan所指出的那样,JBoss EAP 7.1(注意没有Web应用程序,因此没有servlet,我是在EJB 3.2项目中进行的)我不得不添加"value"属性,因为我遇到了一个异常,提示需要"value"属性。
这对我有用。
    @ApplicationPath(value="/*")
        public class MyApplication extends Application {

            private Set singletons = new HashSet();

            public MyApplication() {
                singletons.add(new MyService());
            }

            ...
    }

堆栈跟踪

    Caused by: java.lang.annotation.IncompleteAnnotationException: javax.ws.rs.ApplicationPath missing element value
        at sun.reflect.annotation.AnnotationInvocationHandler.invoke(AnnotationInvocationHandler.java:80)
        at com.sun.proxy.$Proxy141.value(Unknown Source)
        ... 21 more

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接