Несколько месяцев назад я боролся за некоторые 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, поэтому реализации должны предоставлять конструктор по умолчанию.
"serial")public class MyAction implements Action<MyResult> {private String message;// for serialization onlypublic MyAction() {}public MyAction(String message) {this.message = message;}public String getMessage() {return this.message;}} (
"serial")public class MyResult implements Result {private String message;// 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 { 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 :
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' />