4

使用最新最好的 Spring 3.2.3、Tomcat 7.0.42、Servlet 3.0 容器、Java 7。我们需要为响应执行 JSONP,因此我们通过执行 Servlet Filter 来实现它,如下所示:

http://jpgmr.wordpress.com/2010/07/28/tutorial-implementing-a-servlet-filter-for-jsonp-callback-with-springs-delegatingfilterproxy/

但目前没有 web.xml..我们正在使用 Java Annotation Config。

在 our@Controller中,我们返回一个DeferredResult<String>,然后在@Service由 our 调用的 our@Controller中,我们有@Async注解。我们走这@Async条路线(不是 AysncContext 路线)的原因是因为这是一个“即发即弃”类型的异步操作。但我确实需要向请求者返回一些表明操作已开始的 JSON。

@RequestMapping(method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public DeferredResult<String> testing(@RequestParam(value="submit", required=true) String param)
{
            final DeferredResult<String> result = new DeferredResult<String>();
            service.doIt(param, result);

            return result;
}

而在我们的@Service

@Async
public DeferredResult<String> doIt(String param2, DeferredResult<String> result)
{
    log.info("Calling Async doIt Method");
    result.setResult("{hello:there}");
}

我被卡住了...我想弄清楚是否有办法在 Java Config 中为 Servlet 过滤器添加支持异步的标签?我基于看到此演示文稿(幻灯片 28):

http://rstoyanchev.github.io/spring-mvc-32-update/#28

其他一些信息:

在配置上,我们@EnableAsync正在使用 AbstractAnnotationConfigDispatcherServletInitializer 来定义我们的应用程序,但我似乎无法弄清楚如何告诉它使用异步过滤器(仅使用getServletFilters()返回我们的 JsonpFilter)。

基本上发生的事情是JSONP过滤器没有“包装”任何东西......但我确实看到过滤器第二次被调用,在TaskExecutor线程中设置结果之后......但此时响应已经消失...所以,我看到过滤器“获取数据”两次..在@Controller方法退出时一次,然后在设置 DeferredResult.setResult() 后第二次。

我们也在我们的过滤器类上尝试了这个:

@WebFilter(urlPatterns = "/*", asyncSupported = true)

但这不起作用(甚至看起来它甚至根据日志制作了 2 个单独的过滤器..但是,我可能错了)。

如果需要,我们可以切换到 Callable ......这没什么大不了的。我确实看到 DeferredResult 和 Callable 在 Spring 所知道的线程方面存在一些差异。

如果答案是您需要使用 web.xml,那么有什么真正好的方法可以使用 web.xml 混合进行 Java Config?

编辑1:

基于阅读一些不同的资源,我发现了几件事:

  1. 来自:Spring Aync 预览博客

Servlet 过滤器

*所有 Spring Framework Servlet 过滤器实现都已根据需要进行了修改,以在异步请求处理中工作。至于任何其他过滤器,有些过滤器会起作用——通常是那些进行预处理的过滤器,而其他过滤器则需要修改——通常是那些在请求结束时进行后处理的过滤器。此类过滤器需要识别何时退出初始 Servlet 容器线程,为另一个线程继续处理让路,以及何时将它们作为异步调度的一部分调用以完成处理。**

  1. 来自 Spring MVC 参考。docs,我实际上是 RTFM 的:

使用 DeferredResult 处理异步请求的事件序列原则上是相同的,只是由应用程序从某个线程产生异步结果:(1)控制器返回一个 DeferredResult 并将其保存在某个内存队列或列表中,其中它可以被访问,(2) Spring MVC 启动异步处理,(3) DispatcherServlet 和所有配置的 Filter 退出请求处理线程但响应保持打开,(4) 应用程序从某个线程设置 theDeferredResult 并且 Spring MVC 调度请求回到 Servlet 容器,(5) 再次调用 DispatcherServlet 并使用异步生成的结果继续处理。

所以,基本上,我知道/知道单独的线程会调用过滤器......这不是让我感到困惑的事情......这是如何确定过滤器是否应该修改响应......你可以'不要查看数据的大小,因为 0 字节可能是正确的“答案”。

所以,我现在只写 DispatchType = ASYNC

不太确定长期这样做是否正确..但是,它似乎确实解决了问题。

还有其他建议/想法吗?

4

4 回答 4

2

我通过将@Async 添加到控制器方法来解决这个问题。我的猜测是,这样做会处理幕后的过滤器。

于 2015-01-02T04:12:42.310 回答
2

您只需要在每个 servlet 实例上设置 asyncSupport 标志,如下所示:

@Override
public void onStartup(final ServletContext servletContext)
        throws ServletException {
    final AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
    root.setServletContext(servletContext);
    root.scan("com.my.site");
    root.refresh();
    final Dynamic servlet = servletContext.addServlet("cf-validator",
            new DispatcherServlet(root));
    servlet.setLoadOnStartup(1);
    servlet.addMapping("/api/*");
    servlet.setAsyncSupported(true);
}
于 2014-08-21T14:08:37.917 回答
2

如果您正在扩展 AbstractAnnotationConfigDispatcherServletInitializer,您似乎可以覆盖此方法并设置异步支持:

@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
    registration.setInitParameter("dispatchOptionsRequest", "true");
    registration.setAsyncSupported(true);
}
于 2014-05-02T18:14:30.080 回答
1

您可以在 web.xml 文件中的 servlet 定义中async-supported进行设置:true

<servlet>
    <servlet-name>my-servlet</servlet-name>
    <async-supported>true</async-supported>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
于 2018-03-22T08:26:57.163 回答