Статьи

Рекомендации по архитектуре GWT: учебник по RPC-ориентированному шаблону команд

Несколько месяцев назад я боролся за некоторые RPC GWT, и в какой-то момент я почувствовал, что что-то не так. Я думаю, что у каждого разработчика иногда возникает такое чувство, что «должен быть лучший способ сделать это». Мой первый рефлекс был в том, чтобы гуглить «лучшие практики GWT RPC», и я нашел именно то, что искал в известной презентации, которую Рэй Райан представил на Google I / O 2009:

Google Web Tookit Architecture: лучшие практики для разработки вашего приложения GWT

В этом видео Рэй Райан объясняет, как использовать шаблон команд для вызовов RPC. Я не буду обсуждать преимущества этой практики, я оставлю это ему. Лично меня сразу же убедила красота, и после просмотра этого видео я просто не мог сделать ни одного RPC-звонка в старом стиле, не чувствуя себя грязным внутри.

Вместо того, чтобы реализовывать шаблон самостоятельно, я использовал существующую реализацию: gwt-dispatch . Эта статья поможет вам шаг за шагом использовать эту библиотеку и соответствующие рекомендации, которые я знал. Кроме того, я считаю рабочий код лучшей документацией, поэтому я создал соответствующий проект Google Code:  gwt-dispatch-example . Возможно, вы захотите клонировать репозиторий Mercurial, или вы можете скачать и импортировать папку проекта.

обзор

Вот простой взгляд на то, что происходит:

Здесь клиент создает экземпляр Action и передает его на сервер. Действие сопоставляется с его ActionHandler, который обрабатывает запрос, инкапсулированный в Action, и создает новый результат. Результат затем передается обратно клиенту.

Как вы можете видеть, класс Action является клиентским представлением серверной «Command».

Все вызовы RPC выполняются с помощью единой универсальной службы RemoteService. Давайте посмотрим, как это работает:

Действие передается DispatchAsync, который отправляет его через RPC в DispatchService. Служба делегирует выполнение соответствующему ActionHandler и отправляет обратно созданный Result для обработки AsyncCallback. 

При использовании gwt-dispatch вы создадите свои собственные наборы действий, действий и результатов. Однако стандартные реализации Dispatch предусмотрены как для клиента, так и для сервера.

Создание Action, Response и ActionHandler 

Сначала нам нужно создать реализации Action и Response в общем пакете. Мы будем использовать очень простой класс Action. Интерфейсы Action и Response расширяют java.io.Serializable, поэтому реализации должны предоставлять конструктор по умолчанию.

@SuppressWarnings("serial")public class MyAction implements Action<MyResult> {private String message;@Deprecated// for serialization onlypublic MyAction() {}public MyAction(String message) {this.message = message;}public String getMessage() {return this.message;}}
@SuppressWarnings("serial")public class MyResult implements Result {private String message;@Deprecated// for serialization onlypublic MyResult() {}public MyResult(String message) {this.message = message;}public String getMessage() {return message;}}

Наконец, класс ActionHandler в пакете сервера:

public class MyActionHandler implements ActionHandler<MyAction, MyResult> {public MyResult execute(MyAction action, ExecutionContext context) throws ActionException {return new MyResult("Got message: " + action.getMessage());}public Class<MyAction> getActionType() {return MyAction.class;}public void rollback(MyAction action, MyResult result, ExecutionContext context) throws ActionException {// implement this if you want to use this action in compound actions}}

Метод getActionType используется для привязки ActionHandler к его обработчику. Пока не обращайте внимания на параметр ExecutionContext и метод отката, просто помните, что в конечном итоге они понадобятся вам при использовании CompoundActions .

Серверная проводка

Как вы, наверное, догадались, есть кое-что сделать. К счастью, gwt-dispatcher был разработан, чтобы вводить большую часть его автоматически, используя отличные инструменты.

Google Guice

Я мечтал об этом, Google сделал это. Легкий, безопасный с точки зрения типов, простой и увлекательный инструмент Dependency Injection для Java. Просто отлично. Посмотрите  видео и убедитесь сами.

Guice работает с классами « Модуль », где вы настраиваете все свои привязки. Мы собираемся использовать ServletModule, чтобы позволить Guice создать наш DispatchServiceServlet, который будет принимать вызовы RPC и делегировать вызовы соответствующим ActionHandlers. Это альтернативный подход к отображению сервлета в web.xml.

public class DispatchServletModule extends ServletModule {@Overridepublic void configureServlets() {serve("/gwt_dispatch_example/dispatch").with(DispatchServiceServlet.class);}}

 Мы также будем использовать ActionHandlerModule, специальный модуль для привязки ActionHandlers:

public class MyActionHandlerModule extends ActionHandlerModule {protected void configureHandlers() {bindHandler(MyActionHandler.class);}}

Это объявление кажется странным, вы, вероятно, ожидали чего-то вроде:

bindHandler(MyAction.class, MyActionHandler.class);

Но помните, что мы определили соответствующее действие в MyActionHandler.getActionType ().

Now, we have two modules to configure our Injector with. We use GuiceServletContextListener to make sure an injector is created when the application is deployed :

public class MyGuiceServletContextListener extends GuiceServletContextListener {protected Injector getInjector() {return Guice.createInjector(new MyActionHandlerModule(), new DispatchServletModule());}}

Finally, add this to your web.xml to register the GuiceServletContextListener :

<filter><filter-name>guiceFilter</filter-name><filter-class>com.google.inject.servlet.GuiceFilter</filter-class></filter><filter-mapping><filter-name>guiceFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping><listener><listener-class>fr.micouz.gwt.dispatch.server.guice.MyGuiceServletContextListener</listener-class></listener>

 

Client-side Wiring

Google Gin

Gin (GWT Injection) is built on top of Guice and handles injection for client-side GWT code. They share almost the same interface, and used together they bring a very nice consistency.

From here it is going to be very simple. We define a Ginjector interface and set the GinModule to a standard gwt-dispatch module :

@GinModules(ClientDispatchModule.class)public interface MyGinjector extends Ginjector {public DispatchAsync getDispatchAsync();}

That’s it, we can now inject a standard DispatchAsync everywere we need to.

MyGinjector injector = GWT.create(MyGinjector.class);DispatchAsync dispatcher = injector.getDispatchAsync();

There’s another optional practice Ray Ryan introduced : creating your own AsyncCallback. It is great for encapsulating error handling :

public abstract class GotMyResult implements AsyncCallback<MyResult> {public void onFailure(Throwable caught) {Window.alert( "Error: " + caught.getMessage() );}public void onSuccess(MyResult myResult) {got(myResult);}public abstract void got(MyResult result);}

The best part is this : how your client code will make RPC calls :

dispatcher.execute(new MyAction("message"), new GotMyResult() {public void got(MyResult result) {Window.alert( "Received Result: "result.getMessage() );}});

If you haven’t checked the example project by now, now is the time.

One last thing, for your own projects don’t forget to inherit the following dependencies :

    <inherits name="com.google.gwt.inject.Inject" />    <inherits name='net.customware.gwt.dispatch.Dispatch' />