Spring Начальная обработка контроллера после отправки ответа
Я использую контроллер Spring MVC и хочу начать выполнение задачи в новом потоке. Однако задача должна начинаться не сразу, а только после того, как ответ будет отправлен клиенту.
Последовательность - в строгом временном порядке:
- запрос
- вернуть новый
ResponseEntity...
/клиент получает статус HTTP 200 OK. - обработка задания начинается.
Как мне этого добиться?
Я хотел использовать асинхронную абстракцию Spring, вызывая метод, аннотированный @Async, но он не гарантирует, что новый поток будет ожидать отправки ответа первым.
Ответы
Ответ 1
Вы можете использовать перехватчик для этого. Порядок событий для обработки запроса в Spring MVC:
- DispatcherServlet получает пару Request, Response и определяет обработку
- [опционально] вызывается preHandle перехватчиков (с возможностью остановки обработки)
- контроллер называется
- [опционально] называются перехватчики postHandle
- ViewResolver и view выполняют фактическую обработку ответа и отправляют ответ
- [опционально] называются перехватчики afterCompletion
afterCompletion
слишком упрощено и предназначено только для того, чтобы показать, что методы afterCompletion
перехватчика afterCompletion
после отправки ответа клиенту со следующей сигнатурой:
void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex)
throws Exception
В этом методе вы можете проверить возникновение исключения и правильность ответа (ex == null && response.getStatus() == HttpServletResponse.SC_OK
) перед началом обработки.
Ответ 2
Если ваше требование "после ответа отправлено" выполнено с "после того, как представление было обработано", вы можете использовать реализацию HandlerInterceptor. Для примера ср. Учебник Spring 3 MVC Interceptor с примером запуска вашей работы в afterCompletion
.
Если ваша работа должна быть запущена "после того, как она сработает", я хотел бы знать, почему.
Ответ 3
HandlerInterceptor
является решением, но код становится немного сложнее, чем ожидалось.
Вот кодовое предложение, чтобы упростить его, поместив все решение в один класс:
private static final ThreadLocal<Object> result = new ThreadLocal<Object>();
@RequestMapping("/mypath")
public Object execute() throws Exception {
Object obj = new Object();
result.set(obj); // Save the object to be used after response
return obj;
}
@Bean
public MappedInterceptor interceptor() {
return new MappedInterceptor(Arrays.array("/mypath"), new HandlerInterceptor() {
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// Get the saved object
Object results = result.get();
// Clean for the next request
result.set(null);
// TODO Your code to be executed after response.
}
});
}
Ответ 4
Вы можете добавить задачу в очередь блокировки, прежде чем объект ответа будет создан. Пусть исполнитель задачи запускается периодически (каждые х секунд) по очереди и опроса для задач. Если задача найдена, она будет выполнена. Если нет, поток заканчивает свой метод запуска и ждет следующего прогона (через х секунд).
Как периодически запускать задачу:
http://www.mkyong.com/java/how-to-run-a-task-periodically-in-java/
Ввести очередь как зависимость как в контроллере, так и в службе исполнителя задачи. Это должно быть легкое решение для начала.
В этом szenario вы не можете быть уверены, что клиент получает запрос. Но если вы хотите быть в безопасности (r), добавьте дату выполнения к объекту задачи с достаточным смещением (например, текущее время + 30 секунд). Пусть исполнитель задачи проверяет, соответствует ли дата выполнения опроса заданной или прошлой. В противном случае игнорировать задачу для этого прогона.