Отложенный результат в Spring MVC возвращает неверный ответ

Я использую spring mvc 3.2.4 и jquery 1.9.0 для длительного опроса. Мое приложение развернуто на Tomcat 7.0.42. Мои файлы конфигурации spring приведены ниже:

Приложение Web.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee"   
  version="3.0">

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

    <servlet>
        <servlet-name>app</servlet-name>

        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>

        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>

    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

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

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

</web-app>

Spring Конфигурация xml как: -

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.2.xsd">
        <context:annotation-config/>
        <mvc:annotation-driven/>  
        <mvc:default-servlet-handler/>
        <context:component-scan base-package="com.webchat"/>
        <bean id="defferedResult" class="com.exp.DeferredResultContainer"></bean>
</beans>

Контроллер для отправки данных выглядит как

@RequestMapping(value = "/postComment", method = RequestMethod.POST)
public @ResponseBody String postComment(HttpServletRequest request) {
    deferredResultContainer.updateAllResults(request.getParameter("comment"));
    return "success";
}

Класс контейнера отложенного результата

public class DeferredResultContainer {
    private final Set<DeferredResult<String>> deferredResults =   Collections.synchronizedSet(new HashSet<DeferredResult<String>>() ); 

    public void put(DeferredResult<String> deferredResult){ 
        deferredResults.add(deferredResult); 
    } 

    public void updateAllResults(String value){
        for (DeferredResult<String> deferredResult : deferredResults){ 
            deferredResult.setResult(value); 
        }
    }

    public void remove(DeferredResult<String> deferredResult){ 
        deferredResults.remove(deferredResult); 
    } 

    public int determineSize(){
        return deferredResults.size();
    }
}

Контроллер для отложенного результата выглядит как

 @RequestMapping(value = "/getComments", method = RequestMethod.GET)
 @ResponseBody
 public DeferredResult<String> getComments() throws Exception{
     final DeferredResult<String> deferredResult= new DeferredResult<String>(); 
     deferredResultContainer.put(deferredResult);
     deferredResult.onTimeout(new Runnable() {

         @Override public void run() {
             deferredResultContainer.remove(deferredResult);
         }
     });

     deferredResult.onCompletion(new Runnable() { 
         @Override public void run() { 
             deferredResultContainer.remove(deferredResult); 
         } 
     });
     return deferredResult;
 }

Когда я пытаюсь долго опросить его через ajax, я получаю следующий ответ: -

{"setOrExpired":false}

И метод onCompletion также не выполняется.

Просто вещи, находящиеся под контроллером, дают идеальный ответ, как { "1": "2" }

@RequestMapping(value = "/test1", method = RequestMethod.GET)
@ResponseBody
public Map test1() throws Exception{
     Map m1 = new HashMap<String, Object>();
     m1.put("1", "2");
     return m1;
}

Как только я изменю его ниже и добавлю результат отсрочки, я получаю ответ как { "SetOrExpired": истинно}

@RequestMapping(value = "/test", method = RequestMethod.GET)
@ResponseBody
public DeferredResult<Map> test() throws Exception{
    DeferredResult<Map> result = new DeferredResult<Map>();
     Map m1 = new HashMap<String, Object>();
     m1.put("1", "2");
     result.setResult(m1);
     return result;
}

Код опроса

$(document).ready(function() {
    longPoll();

    function longPoll(){
         $.support.cors = true;
        var path = "http://localhost:8080/WebChatExp/rest";
         $.ajax({
            url: path + "/getComments",
            cache:false,
            success: function(data){
                //To Do
                            alert("Data" + JSON.stringify(data));
            },
            error: function(err, status, errorThrown ) {

            },
            type: "GET",
            dataType: "json",
            complete: longPoll,
            timeout: 60000 // timeout every one minute
        }); 
    }

Я искал различные примеры, но не могу понять, требуется ли дополнительная настройка для отложенного результата. Просьба сообщить.

Ответы

Ответ 1

Тело ответа, которое вы получаете

{"setOrExpired":true}

указывает, что Spring сериализовал ваш DeferredResult (который имеет различные свойства, включая setOrExpired), для JSON вместо обработки его с помощью DeferredResultMethodReturnValueHandler. Другими словами, он использовал другой HandlerMethodReturnValueHandler, скорее всего RequestResponseBodyMethodProcessor (который обрабатывает @ResponseBody), чтобы обрабатывать значение, возвращаемое с вашего @RequestMapping метод аннотированного обработчика. (Самый простой способ проверить это - увидеть, что происходит, когда вы удаляете аннотацию @ResponseBody.)

Глядя на 3.2.x исходный код RequestMappingHandlerAdapter, который регистрирует экземпляры HandlerMethodReturnValueHandler по умолчанию, зарегистрирован DeferredResultMethodReturnValueHandler до RequestResponseBodyMethodProcessor и, следовательно, сначала обработает возвращаемое значение DeferredResult.

Поскольку вы видите другое поведение, мы должны предположить, что ваша конфигурация не то, что вы здесь показали. (Заметим, что <mvc:annotation-driven/> регистрирует a RequestMappingHandlerAdapter.)


Также обратите внимание, что вы в настоящее время загружаете конфигурацию в /WEB-INF/app-servlet.xml дважды, один раз - ContextLoaderListener и один раз - DispatcherServlet.

Извлеките свой ContextLoaderListener и соответствующий context-param полностью. Они не нужны в вашем примере.