Шаблон проектирования для обработки асинхронного ответа в Java - PullRequest
17 голосов
/ 03 августа 2011

Я читаю ответы из аналогичных вопросов и ответов

Как создать асинхронный HTTP-запрос в JAVA? | Шаблон проектирования асинхронного программирования |
AsyncTask Android - Шаблон проектирования и возвращаемые значения

Я вижу много решений, но ни одно из них меня не удовлетворяет.

Способ прослушивания

Как только результаты получены, обработка выполняется в методе onResult.

public interface GeolocationListener {
public void onResult(Address[] addresses);
public void onError(Exception e);
}

Это решение не совсем меня удовлетворяет, потому что я хочу обработать результаты в основном методе.Я ненавижу этот интерфейс, потому что когда ответ возвращается, он обрабатывается в onResult, что приводит к цепочкам обработки и невозможности вернуться к методу "main".

Способ сервлета

public class SignGuestbookServlet extends HttpServlet {

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws IOException {
        // ...
        resp.sendRedirect("/guestbook.jsp");
    }
}

Нет открытого кода Java, вызывающего сервлет.Все настройки выполняются в файле web.xml

Как я хочу

Дождитесь ответа, подобного этому

Response a = getResponse();
// wait until the response is received, do not go further
// process
Response b = getResponse();
// wait until the response is received, do not go further
process(a,b);

Есть лиразработать шаблон для обработки асинхронного запроса и ожидания ответа, как указано выше?Другой способ, чем слушатель.Пожалуйста, не библиотека или рамки.

РЕДАКТИРОВАТЬ Спасибо за ответы.Я не дал вам полную картину, поэтому я показал класс Geolocation, я начал реализацию.Я не знаю, как реализовать метод.Может кто-то показывает "как"?Он (или она) также должен реализовать слушателя для получения результатов

private Address getFullAddress (String text, AddressListener listener, ... ){

    // new Geolocation(text, listener, options).start() 
    // implements Geolocation.GeolocationListener   
    // how to return the Address from the onResult ?
}

Ответы [ 5 ]

11 голосов
/ 03 августа 2011

Во-первых, вы не должны отвергать первые два метода, которые вы обсуждали.Есть очень веские причины, по которым люди используют эти методы, и вы должны попытаться изучить их, а не создавать новые.

В противном случае вам следует обратиться к java.util.concurrent :

ExecutorService es = Executors.newFixedThreadPool(2);
...
Future<Response> responseA = es.submit(responseGetter);
Future<Response> responseB = es.submit(responseGetter);

process(responseA.get(), responseB.get());

, где responseGetter имеет тип Callable<Response> (необходимо реализовать метод public Response call()).

9 голосов
/ 03 августа 2011

Асинхронный код всегда можно сделать синхронным. Самый простой / грубый способ - сделать асинхронный вызов, а затем ввести цикл while, который просто спит в текущем потоке, пока значение не вернется.

Редактировать: Код, который превращает асинхронный обратный вызов в синхронный код - опять же, грубая реализация:

import java.util.concurrent.*;

public class MakeAsynchronousCodeSynchronous {
    public static void main(String[] args) throws Exception {
        final Listener listener = new Listener();
        Runnable delayedTask = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    throw new IllegalStateException("Shouldn't be interrupted", e);
                }
                listener.onResult(123);

            }
        };
        System.out.println(System.currentTimeMillis() + ": Starting task");
        Executors.newSingleThreadExecutor().submit(delayedTask);
        System.out.println(System.currentTimeMillis() + ": Waiting for task to finish");
        while (!listener.isDone()) {
            Thread.sleep(100);
        }
        System.out.println(System.currentTimeMillis() + ": Task finished; result=" + listener.getResult());
    }

    private static class Listener {
        private Integer result;
        private boolean done;

        public void onResult(Integer result) {
            this.result = result;
            this.done = true;
        }

        public boolean isDone() {
            return done;
        }

        public Integer getResult() {
            return result;
        }
    }
}

Вы также можете использовать CountDownLatch, как рекомендовано ответом hakon. Это будет делать в основном то же самое. Я бы также предложил вам ознакомиться с java.util.concurrent пакетом для лучшего способа управления потоками. Наконец, только то, что вы можете сделать это, не делает это хорошей идеей. Если вы работаете с фреймворком, основанным на асинхронных обратных вызовах, вам, вероятно, гораздо лучше узнать, как эффективно использовать фреймворк, чем пытаться разрушить его.

6 голосов
/ 03 августа 2011

Может CountDownLatch помочь вам? В методе main вы вызываете getResponse, а затем countDownLatch.await (). Передайте защелку обратного отсчета методу getResponse, а затем выполните обратный отсчет, как только getResponse завершит результат getResponse:

CountDownLatch latch = new CountDownLatch(1);
Response a = getResponse(latch);
latch.await();

latch = new CountDownLatch(1);
Response b = getResponse(latch);
latch.await();

process(a, b);

Ваш getResponse должен вызвать latch.countDown (), как только асинхронные части вернут результат.

например:.

public Response getResponse(CountDownLatch latch) {
     someAsychBloc(final CountDownLatch latch) {
       do work
       latch.countDown();
    }
}
1 голос
/ 03 августа 2011

По сути, вам нужен своего рода «слушатель», несмотря ни на что.Это потому, что вы не знаете, КОГДА ваше возвращаемое сообщение будет возвращаться, если оно вообще будет (это один из недостатков асинхронной обработки ... что делать, если вы не получаете ответное сообщение).

Таким образом, вам либо нужно реализовать прослушиватель, который ожидает событий (т. Е. Он подталкивается возвращаемым сообщением, которое нужно обработать).

Или вы можете создать гибрид с этим, имея отдельный поток, который "опрашивает"(или вытягивает) область ответа в вашем сервисе, чтобы проверить, существует ли ответное сообщение.

Таким образом, все сводится к тому, хотите ли вы использовать метод «извлечения» или «выталкивания» для получения сообщений.

Платформа SCA (Service Component Architecture) может быть чем-то, что нужно учитывать, но в зависимости от того, что вы делаете, это может быть также излишним.Но нужно учесть.

РЕДАКТИРОВАТЬ:

Я только что нашел это в Java SE 6 Javadocs, которые могут быть полезны.Интерфейс CompletionService , который абстрагирует то, о чем вы заботитесь -> асинхронную работу.Я предлагаю вам взглянуть.

0 голосов
/ 03 августа 2011

Если вам нужен поток страниц в веб-приложении, вы должны обрабатывать его в Интернете: хранить некоторые данные либо в сеансе, либо в файлах cookie или скрытых полях и т. Д.

Проблема, с которой вы столкнулисьпопытка заняться, насколько я понимаю, не из-за асинхронности, а из-за httpcocole без состояния.

С уважением, Стефан

...