Отложенный результат в 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
полностью. Они не нужны в вашем примере.