如何在Java Config中定义Spring 3.2异步Servlet-Filter而不使用web.xml

时间:2013-07-19 22:04:29

标签: spring spring-mvc jsonp servlet-filters

使用最新最好的Spring 3.2.3,Tomcat 7.0.42,Servlet 3.0容器,Java 7.我们需要为我们的响应做JSONP,所以我们通过像这样做一个Servlet过滤器实现它:

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

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

在我们的@Controller中,我们将返回DeferredResult<String>,然后在我们@Service调用的@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过滤器添加 async-supported 标记?我的基础是看到这个演示文稿(幻灯片28):

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

其他一些信息:

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

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

我们也在Filter类上尝试了这个:

@Controller

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

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

如果答案是您需要使用web.xml,那么使用web.xml混合执行Java Config的任何非常好的方法都可用吗?

编辑1:

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

  1. 来自:Spring Aync预览博客
  2.   

    Servlet过滤器

         
        

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

      
    1. 来自Spring MVC Ref。 docs,我实际上是RTFM:
    2.   

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

      所以,基本上,我知道/知道单独的线程会调用过滤器...这不是什么让我感到高兴..这是如何判断过滤器是否应该修改响应...和你无法查看数据的大小,因为0字节可能是正确的“答案”。

      所以,我现在只写DispatchType = ASYNC

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

      还有其他建议/想法吗?

4 个答案:

答案 0 :(得分:2)

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

@Override
protected void customizeRegistration(ServletRegistration.Dynamic registration) {
    registration.setInitParameter("dispatchOptionsRequest", "true");
    registration.setAsyncSupported(true);
}

答案 1 :(得分:2)

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

答案 2 :(得分:1)

您只需要在每个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);
}

答案 3 :(得分: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>