我正在尝试在嵌入式Jetty设置中为Jersey设置LoggingFilter。使用的粘合代码如下:

ServletContainer servletContainer = new ServletContainer(application);
ServletHolder servletHolder = new ServletHolder(servletContainer);
servletHolder.setInitParameter("com.sun.jersey.config.feature.Debug", "true");
servletHolder.setInitParameter("com.sun.jersey.config.feature.Trace", "true");
servletHolder.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters",
  "com.sun.jersey.api.container.filter.LoggingFilter");
servletHolder.setInitParameter("com.sun.jersey.spi.container.ContainerResponseFilters",
  "com.sun.jersey.api.container.filter.LoggingFilter");

但是日志过滤器实际上被忽略了,我在控制台中看不到任何相关的日志。我怎样才能做到这一点?在Jersey 1.x和2.x上都进行了测试。

relevant answer描述了如何使用web.xml实现此目的。

最佳答案

如果不是彻底的错误,我认为这是ServletContainer的已记录行为的非常细微的差别。关于初始化参数的 ServletContainer docs读为:



答案隐藏在那里。具体来说,如果ResourceConfig实例不是由ServletContainer创建的,则Servlet初始化参数不会作为属性添加,因此不会影响应用程序的配置。当您提供自己的Application实例时(与new ServletContainer(application)一样),初始化大致遵循以下过程:

您的代码使用ServletContainer实例调用以下Application构造函数:

public ServletContainer(Application app) {
    this.app = app;
}

容器将ServletContainer初始化为典型的Servlet生命周期的一部分:
protected void init(WebConfig webConfig) throws ServletException {
    webComponent = (app == null)
            ? new InternalWebComponent()
            : new InternalWebComponent(app);
    webComponent.init(webConfig);
}

您的Application实例进入InternalWebComponent构造函数。 InternalWebComponent只是WebComponent的轻微定制,因此:
InternalWebComponent(Application app) {
    super(app);
}

致电:
public WebComponent(Application app) {
    if (app == null)
        throw new IllegalArgumentException();

    if (app instanceof ResourceConfig) {
        resourceConfig = (ResourceConfig) app;
    } else {
        resourceConfig = new ApplicationAdapter(app);
    }
}

在这里,因为您直接提供了Application实例,所以在第二个ResourceConfig的分支之一中为您构建了if。构造完成后,立即在新组件上调用WebComponent.init()(请引用上面的ServletContainer.init()调用,它来自于此)。在init()调用内部,将创建文档所引用的“创建的ResourceConfig”,但是在您的情况下,已经存在一个,如我们到达此处的跟踪所示。也就是说,resourceConfig不为null,因此下面的重要行不会执行:
public void init(WebConfig webConfig) throws ServletException {
    ...
    if (resourceConfig == null)
        resourceConfig = createResourceConfig(config);
    ...
}

createResourceConfig()方法(仍在WebComponent中)读取为:
private ResourceConfig createResourceConfig(WebConfig webConfig)
        throws ServletException {
    final Map<String, Object> props = getInitParams(webConfig);
    final ResourceConfig rc = createResourceConfig(webConfig, props);
    rc.setPropertiesAndFeatures(props);
    return rc;
}

您可以在该调用中看到setPropertiesAndFeatures()用于将servlet的初始化参数复制到ResourceConfig实例中。不幸的是,这是唯一可以进行调用的地方,在您的情况下,执行从未在这里进行,这主要是因为您使用了非默认的ServletContainer构造函数之一。

我希望原始作者只使用一个无参数构造函数编写ServletContainer,之后再添加另外两个,以便于与Servlet 3.0容器一起使用,而又没有意识到这种行为。否则,我希望能在文档中看到一些提及。

因此,长话短说:要么使用默认的ServletContainer构造函数,要么自行找到一种方法来照顾这一部分:
Map<String, Object> props = getInitParams(webConfig);
rc.setPropertiesAndFeatures(props);

第一种方法可能是最简单的。例如,您也可以将Application类指定为init参数,只要没有什么要求您提前实例化它,例如:
servletHolder.setInitParameter("javax.ws.rs.Application", "org.foo.MyApplication");

这样,将采用“常规”初始化路径,这意味着WebComponent将为您创建ResourceConfig并正确应用init参数。

10-05 21:00