【问题标题】:Multiple jersey servlets in one single web.xml一个 web.xml 中的多个 jersey servlet
【发布时间】:2012-07-12 21:29:20
【问题描述】:

是否可以在一个 web.xml 中包含多个 jersey servlet?我正在尝试以这种方式进行 RESTfull 版本控制:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">

  <display-name>myapi</display-name>

  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/context-v1.xml /WEB-INF/context-v2.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <servlet>
    <servlet-name>REST-V1</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.myapi.rest.v1</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>REST-V1</servlet-name>
    <url-pattern>/v1/*</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>REST-V2</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
      <param-name>com.sun.jersey.config.property.packages</param-name>
      <param-value>com.myapi.rest.v2</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>REST-V2</servlet-name>
    <url-pattern>/v2/*</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>

</web-app>

但是spring context-v1和context-v2应该分开加载?因为他们有豆子,它们有相同的名字等等。

编辑:

如果您查看我的控制台输出,它会为每个 servlet 加载资源(管理员/信息)两次:

15.07.2012 14:47:08 com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
  com.myapi.rest.v1
15.07.2012 14:47:08 com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
  class com.myapi.rest.v1.LOAdminResource
  class com.myapi.rest.v1.LOInfoResource
15.07.2012 14:47:08 com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.servlet.SpringServlet getContext
INFO: Using default applicationContext
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
INFO: Registering Spring bean, adminResource_v2, of type com.myapi.rest.v2.LOAdminResource as a root resource class
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
INFO: Registering Spring bean, infoResource_v2, of type com.myapi.rest.v2.LOInfoResource as a root resource class
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
INFO: Registering Spring bean, adminResource_v1, of type com.myapi.rest.v1.LOAdminResource as a root resource class
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
INFO: Registering Spring bean, infoResource_v1, of type com.myapi.rest.v1.LOInfoResource as a root resource class
15.07.2012 14:47:09 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.8 06/24/2011 12:17 PM'
15.07.2012 14:47:09 com.sun.jersey.api.core.PackagesResourceConfig init
INFO: Scanning for root resource and provider classes in the packages:
  com.myapi.rest.v2
15.07.2012 14:47:09 com.sun.jersey.api.core.ScanningResourceConfig logClasses
INFO: Root resource classes found:
  class com.myapi.rest.v2.LOAdminResource
  class com.myapi.rest.v2.LOInfoResource
15.07.2012 14:47:09 com.sun.jersey.api.core.ScanningResourceConfig init
INFO: No provider classes found.
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.servlet.SpringServlet getContext
INFO: Using default applicationContext
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
INFO: Registering Spring bean, adminResource_v2, of type com.myapi.rest.v2.LOAdminResource as a root resource class
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
INFO: Registering Spring bean, infoResource_v2, of type com.myapi.rest.v2.LOInfoResource as a root resource class
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
INFO: Registering Spring bean, adminResource_v1, of type com.myapi.rest.v1.LOAdminResource as a root resource class
15.07.2012 14:47:09 com.sun.jersey.spi.spring.container.SpringComponentProviderFactory registerSpringBeans
INFO: Registering Spring bean, infoResource_v1, of type com.myapi.rest.v1.LOInfoResource as a root resource class
15.07.2012 14:47:09 com.sun.jersey.server.impl.application.WebApplicationImpl _initiate
INFO: Initiating Jersey application, version 'Jersey: 1.8 06/24/2011 12:17 PM'

【问题讨论】:

  • load-on-startup 应该——如果我没记错的话——在这两种情况下都是 1
  • 不,这不是真的,它是 servlet 的启动顺序...

标签: java spring rest jersey


【解决方案1】:

是的,您可以在 web.xml 中指定两个或多个 servlet。请记住为每一个指定不同的 servlet 映射。

<servlet>
    <servlet-name>servletOne</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.packageOne</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet>
    <servlet-name>servletTwo</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>com.packageTwo</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>ServletOne</servlet-name>
    <url-pattern>/v1/*</url-pattern>
</servlet-mapping>
    <servlet-mapping>
    <servlet-name>ServletTwo</servlet-name>
<url-pattern>/v2/*</url-pattern>
</servlet-mapping>

initParameter loadOnStartup 定义了加载 servlet 的顺序(在这种情况下,首先是 servletOne,然后是 servletTwo)。

【讨论】:

  • 没错,两个 servlet 都已加载。但请查看我的控制台输出。我的问题是我的资源(AdminResource、InfoResource)为每个 servlet 加载了两次...
  • mmm... 很奇怪,因为您已经定义了两个不同的包... 或者您可以尝试以编程方式定义服务器和 Servlet(如此处定义的代码 stackoverflow.com/questions/11210734/… 在 Start Server Android Activity 下)
  • 这不是正确答案!无论您放什么,Spring 都会加载它在主上下文中找到的每个带注释的类。因此,您将拥有两个提供完全相同的东西的 servlet。当心并尝试一下!正确答案在上面……
【解决方案2】:

问题是,当您同时使用 Jersey 和 Spring 时,Jersey/Spring servlet 会遍历所有可用的 Spring bean 并注册它将在其中找到的每个资源和提供程序类。

如果您有多个 Jersey/Spring servlet 使用相同的(根)上下文并因此共享 bean 定义,则为每个此类 servlet 执行该过程,并且资源和提供者类被注册多次。

为了避免多次注册同一个 bean,请在相应 Jesrey/Spring servlet 的子上下文中定义此类 bean。

甚至不需要为在 web.xml 中声明类提供初始化参数,除非需要混合使用 Spring 管理和 Jersey 管理的类。

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/root-context.xml</param-value>
</context-param>

...

<servlet>
    <servlet-name>REST-V1</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/context-v1.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet>
    <servlet-name>REST-V2</servlet-name>
    <servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/context-v1.xml /WEB-INF/context-v2.xml</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
</servlet>

...

【讨论】:

  • 这应该是选择的答案。
【解决方案3】:

我知道这个话题已经过时了。但我的回答可以帮助别人。

我们可以用这些分隔符在web.xml中配置多个资源包:

  1. 空白
  2. 逗号 (,)
  3. 分号 (;)
  4. 下一行

示例:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>MultiplePackageRest</display-name>
  <servlet>
    <servlet-name>JerseyMultiplePackage</servlet-name>
    <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
    <init-param>
      <param-name>jersey.config.server.provider.packages</param-name>
      <param-value>info.javadoff.rest1,info.javadoff.rest2,...</param-value>
    </init-param>
      <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>JerseyMultiplePackage</servlet-name>
    <url-pattern>/api/*</url-pattern>
  </servlet-mapping>
</web-app>

【讨论】:

    【解决方案4】:

    另一种可能性是覆盖com.sun.jersey.spi.spring.container.servlet.SpringServlet 中的一种方法。启动方法如下所示(1.19.1 版):

    @Override
    protected void initiate(ResourceConfig rc, WebApplication wa) {
        try {
            wa.initiate(rc, new SpringComponentProviderFactory(rc, getContext()));
        } catch (RuntimeException e) {
            LOGGER.log(Level.SEVERE, "Exception occurred when intialization", e);
            throw e;
        }
    }
    

    如果您像这样更改子类中的代码,那么您可以根据您的条件(例如包名称)过滤掉不需要的 spring bean:

    @Override
    protected void initiate(ResourceConfig rc, WebApplication wa) {
        try {
            SpringComponentProviderFactory springComponentProviderFactory = new SpringComponentProviderFactory(rc, getContext());
            rc.getClasses().removeIf( clazz -> clazz.getPackage().getName().startsWith( "bla" ));
            wa.initiate(rc, springComponentProviderFactory);
        } catch (RuntimeException e) {
            LOGGER.log(Level.SEVERE, "Exception occurred when intialization", e);
            throw e;
        }
    }
    

    有点老套的解决方案,但对我们非常有效。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-01
      • 2023-03-06
      • 2014-03-07
      • 1970-01-01
      • 2017-09-19
      • 2010-12-24
      • 2016-07-05
      相关资源
      最近更新 更多