昨年の
Google I / Oでのスピーチで
、Ray Rayanは多かれ少なかれGWTプロジェクトのアーキテクチャを適切に構築する方法を聴衆に語った。 彼の推奨事項の1つは、コマンドテンプレートを使用してRPCサービスを整理することです。 この記事では、例として最も単純なGWTアプリケーションを使用して、このアプローチを簡単に強調してみます。 gwt-dispatch
GWT-Dispatchライブラリは、RPC呼び出しをディスパッチするために使用されます。 この記事は、いくつかのソース(
GWT-Dispatch入門 、
GWT MVPの例 )の共生、理解、およびコンパイルであることをすぐに警告したいと思います。 GWTアプリケーションを適切に構築する方法のクイックスタートガイドと考えてください。 すべての資料は、RPCサービスのサーバー実装もJava言語で実行されるという事実を考慮して開発されています。
多かれ少なかれ大規模なアプリケーションを開発するときに、設計パターン(パターン)が私たちの助けに駆り立てることは秘密ではありません。 パターンは、特定の典型的なケースを解決するための一種のレシピです。
Wikiのパターンを使用したデザインパターンの研究と適用から始め、関連する書籍、記事、作品などにさらに進むことができます。
つまり、コマンドテンプレートを使用すると、統一されたインターフェイスを介してコマンド(アクションなど)インターフェイスの特定の実装を実行できます。

GWT RPCに関して、このアプローチのアプリケーションは、RPCサービスを呼び出すための1つのインターフェース(ディスパッチャー)を持ち、それに対応するアクション(コマンド)のオブジェクトを送信できるようにします。
必要なライブラリを接続する
したがって、コマンドを使用してRPC相互作用をプロジェクトに実装するには、追加のライブラリをプロジェクトに接続する必要があります。
- GWT-Dispatch-コールマネージャーのGWT実装。 このライブラリは、コマンドとその実行結果を整理するためのActionおよびResultインターフェイスも提供します。
- Google Guice - Googleの依存性注入フレームワーク。 依存性注入アプローチを使用して、サーバーコードで依存性管理を整理できます。 よく知られているSpring Frameworkよりもはるかに単純であるため、より高速に動作します。 デモプロジェクトの実装中、Guiceはサーブレットマネージャーとしても機能し、サーバー側全体のリンクを初期化しました。 しかし、それについては後で。
- Google GIN -GWTのGuice実装。 クライアント(読み取り、GWT)コードでDIアプローチを適用できます。 私たちは明らかにそれを使用しません。依存関係として必要です。
これらのライブラリをプロジェクトに接続するには、ファイルgin-1.0.jar、guice-2.0.jar、guice-servlet-2.0.jarおよびgwt-dispatch-1.0.0.jarをWEB-INF / libに配置して、ビルドパスに追加するだけです。プロジェクト。 接続されたモジュールを使用したGWTモジュールの構成は、私にとって次のようになります。
<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .
<? xml version ="1.0" encoding ="UTF-8" ? > < module rename-to ='rpc_command' > < inherits name ='com.google.gwt.user.User' /> < inherits name ="com.google.gwt.inject.Inject" /> < inherits name ="net.customware.gwt.dispatch.Dispatch" /> < entry-point class ='net.pimgwt.client.RpcCommandEntryPoint' /> < source path ='client' /> </ module > * This source code was highlighted with Source Code Highlighter .
GWT側の特定のチームの編成
1つのRPC呼び出しを作成する例を使用して、チームの作成を説明します。 その本質は、ドアと同じくらい簡単です。入力フィールドから読み取ったパラメーターをサーバーメソッドに送信し、答えを取得します。 それだけです そうそう、受け取った応答をUIに表示します。 デモプロジェクトには、サーバーから偽のDTOオブジェクトの配列を受け取る呼び出しも含まれています。 このノートでは、そのコードは考慮されません。 興味があれば、コメントで追加することができます。
RPCコマンドを作成するには、Actionインターフェイスを実装するクラスを作成する必要があります。
- パッケージnet.pimgwt.client.rpc;
- import net.customware.gwt.dispatch.shared.Action;
- @SuppressWarnings( "serial" )
- パブリック クラス SingleRequestActionはAction <SingleRequestResult>を実装します{
- private String param;
- public SingleRequestAction(){
- }
- public SingleRequestAction(String param){
- this .param = param;
- }
- public String getParam(){
- この .paramを返します。
- }
- }
*このソースコードは、 ソースコードハイライターで強調表示されました。
ご覧のとおり、複雑なことは何もありません。 このコマンドは、サーバーに送信されるパラメーターをカプセル化します。 ここで唯一興味深い点は、実行の結果がSingleRequestResultクラスのオブジェクトになることを示すことです。
- パッケージnet.pimgwt.client.rpc;
- import net.customware.gwt.dispatch.shared.Result;
- @SuppressWarnings( "serial" )
- パブリック クラス SingleRequestResultはResultを実装します{
- private String resultMessage;
- public SingleRequestResult(){}
- public SingleRequestResult(String resultMessage){
- this .resultMessage = resultMessage;
- }
- public String getResultMessage(){
- この .resultMessageを返します。
- }
- }
*このソースコードは、 ソースコードハイライターで強調表示されました。
また、クライアントコードに「到着」するデータをカプセル化します。
現時点では、クライアント側の準備は完了しています。 サーバーで動作するコーヒー豆の焙煎に取り掛かります。 ちなみに、サーバーはGoogle App Engineで実行されます。
RPCサービスのサーバー実装
GWT-Dispatchライブラリは、クライアント部分とサーバー部分の両方のディスパッチャを編成するためのツールを提供します。
RPC呼び出しの処理を処理するサーブレットマネージャーからサーバー側の構成を開始します。
- パッケージnet.pimgwt.server;
- import net.customware.gwt.dispatch.server.service.DispatchServiceServlet;
- import com.google.inject.servlet.ServletModule;
- パブリック クラス DispatcherServletModuleはServletModuleを拡張します{
- @Override
- protected void configureServlets(){
- serve( "/ rpc_command / dispatch" ).with(DispatchServiceServlet。class);
- }
- }
*このソースコードは、 ソースコードハイライターで強調表示されました。
DispatcherServletModuleクラス
ServletModuleます。 GWT-Dispatchによって提供されるサーブレットマネージャー実装のRPC URLと一致する
configureServlets()親メソッドを書き換え
configureServlets() 。 デフォルトでは、ディスパッチャーがリッスンするURLは、application_name / dispatchスキームに従って構築されます。
クライアント側で宣言されたコマンドに添付されるハンドラーを実装します(
SingleRequestActionコマンド):
- パッケージnet.pimgwt.server;
- import net.customware.gwt.dispatch.server.ActionHandler;
- import net.customware.gwt.dispatch.server.ExecutionContext;
- import net.customware.gwt.dispatch.shared.ActionException;
- import net.pimgwt.client.rpc.SingleRequestAction;
- import net.pimgwt.client.rpc.SingleRequestResult;
- パブリック クラス SingleRequestHandlerはActionHandler <SingleRequestAction、SingleRequestResult>を実装します{
- @Override
- public SingleRequestResult execute(SingleRequestActionアクション、ExecutionContext
- コンテキスト)ActionException {
- return new SingleRequestResult( "あなたは入力されました:" + action.getParam());
- }
- @Override
- public Class <SingleRequestAction> getActionType(){
- SingleRequestActionを返します。 クラス ;
- }
- @Override
- public void rollback(SingleRequestActionアクション、SingleRequestResult結果、
- ExecutionContextコンテキスト)ActionException {}をスローします
- }
*このソースコードは、 ソースコードハイライターで強調表示されました。
コマンドハンドラーは、パラメーター化中にコマンドとその結果が示される汎用インターフェイスを実装します。 この場合、それぞれ
SingleRequestActionおよび
SingleRequestResultです。
ActionHandlerインターフェース
ActionHandler 、実行クラスが
execute(), getActionType()および
rollback()メソッドを提供することも
ActionHandlerとします。これらのメソッドの名前は、それ自体を表しています。 上記のコードでは、
SingleRequestActionなどの単純なコマンドの場合、失敗した場合のロールバックアクションは単に空白のままです。 ロールバックするものはありません。
execute()メソッドの結果は
SingleRequestResultオブジェクトです。
SingleRequestResultオブジェクトには、呼び出し側(クライアント)に送信される応答テキストを単純に書き込みます。
さて、
getActionType()メソッドは、ハンドラーがバインドされているコマンドのクラスへの参照を返す必要があります。 これは、ディスパッチャーが他のハンドラーではなく、目的のハンドラーを正しく呼び出すために必要です。
GWT-Dispatchライブラリは、ActionおよびResultインターフェイスを直接ディスパッチして提供することに加えて、Google Guiceとの統合も提供します。 この統合により、Guiceコンテキストでコマンドハンドラーを登録できます。
- パッケージnet.pimgwt.server;
- import net.customware.gwt.dispatch.server.guice.ActionHandlerModule;
- パブリック クラス RpcCommandHandlerModuleはActionHandlerModuleを拡張します{
- @Override
- protected void configureHandlers(){
- bindHandler(SingleRequestHandler。class);
- //。 。 。
- }
- }
*このソースコードは、 ソースコードハイライターで強調表示されました。
GuiceServletContextListenerクラスを使用してすべてを接続します。
GuiceServletContextListenerクラスは、外部から行われていることを「リッスン」し、クライアント/ rpc_command / dispatchから要求が来たときに応答し、対応するコマンドのハンドラーを実行します。
- パッケージnet.pimgwt.server;
- import com.google.inject.Guice;
- import com.google.inject.Injector;
- import com.google.inject.servlet.GuiceServletContextListener;
- パブリック クラス RpcCommandGuiceConfigはGuiceServletContextListenerを拡張します{
- @Override
- 保護されたインジェクターgetInjector(){
- return Guice.createInjector( new RpcCommandHandlerModule()、 new DispatcherServletModule());
- }
- }
*このソースコードは、 ソースコードハイライターで強調表示されました。
GuiceServletContextListenerクラス
GuiceServletContextListener 、Javaサーブレットと統合する手段としてGuiceフレームワークによって提供されます。 上記のコードは、必要なすべての注入(注入)を適切な場所で実行します。 したがって、GWT-DispatchとGuiceの統合チェーンがあり、サーブレットは閉じられます。
このすべてを単一のアンサンブルとして再生するために必要な最後の手順は、web.xmlファイルと対応する要求フィルターで必要なリスナーを示すことです。
- <? xml version = "1.0" encoding = "UTF-8" ? >
- <! DOCTYPE Webアプリ
- PUBLIC "-// Sun Microsystems、Inc.//DTD Web Application 2.3 // JA"
- 「http://java.sun.com/dtd/web-app_2_3.dtd」 >
- < web-app >
- < フィルター >
- < filter-name > guiceFilter </ filter-name >
- < filter-class > com.google.inject.servlet.GuiceFilter </ filter-class >
- </ フィルター >
- < フィルターマッピング >
- < filter-name > guiceFilter </ filter-name >
- < url-pattern > / * </ url-pattern >
- </ filter-mapping >
- < リスナー >
- < listener-class > net.pimgwt.server.RpcCommandGuiceConfig </ listener-class >
- </ リスナー >
- < ようこそファイルリスト >
- < welcome-file > index.html </ welcome-file >
- </ welcome-file-list >
- </ web-app >
*このソースコードは、 ソースコードハイライターで強調表示されました。
GuiceFilter 、クライアントのサーバー側にあるすべての要求をフィルター処理するように構成されています。 当然、url-param-instructionで、リスニング用の独自のURLパターンを指定できます。 私はこれを行う方法を言うつもりはありません、これらは明白なものであり、考慮中の問題に関連していません。
サーバー側の準備ができました。 クライアントコードからのRPC呼び出しをディスパッチャーに関連付けることは今でも残っています。
GWTコードでのコマンドのディスパッチ
GWTコード内のRPCコマンドの呼び出しには、
DispatchAsyncインターフェイスが責任を負います。 たとえば、以前に取得した結果をキャッシュできるディスパッチャとして、このインターフェイスを必要に応じて実装できます。 デモプロジェクトでは、GWT-Dispatchパッケージから
DefaultDispatchAsync 「ボックス化」実装を再度選択しました。
以下に、指定されたインターフェイスを介してRPC呼び出しを開始し、サーバー側から受信した結果を表示するボタンクリックハンドラーのみを示します。
- //。 。 。
- private DispatchAsync rpcDispatcher = new DefaultDispatchAsync();
- //。 。 。
- @UiField Button singleValueTestButton ;
- //。 。 。
- @UiHandler( " singleValueTestButton " )
- public void singleValueButtonClicked(ClickEvent event ){
- responseLable.setText( "" );
- rpcDispatcher.execute( new SingleRequestAction(paramTextbox.getText())、 new
- AsyncCallback <SingleRequestResult>(){
- @Override
- public void onFailure(Throwable catch){
- responseLable.setText( "エラーが発生しました:" +
- caught.getMessage());
- }
- @Override
- public void onSuccess(SingleRequestResult結果){
- responseLable.setText(result.getResultMessage());
- }
- });
- }
*このソースコードは、 ソースコードハイライターで強調表示されました。
ここでの主なポイントは、初期化されたコマンドをディスパッチャに渡し、サーバーに送信することです。 コールバックでは、結果は受信したSingleRequestResponseサーバーから単に取得されます
responseLable.setText(result.getResultMessage());すべてが書かれ、実装され、構成され、機能します!
デモプロジェクト
以下のスクリーンショットは、プロジェクトパッケージパネルのデモプロジェクトの構造を示しています

よく見ると、プロジェクトに別のRPCコマンド
MultiRequestActionが実装されていることがわかります。 実行の結果は
MultiRequestResultであり、これには
DummyDTOオブジェクトのリストが含まれます。このリストは、このコマンドのサーバーハンドラーのループで埋められます。
ライブ視聴可能な
プロジェクトRPC Command Demo Project結論の代わりに
説明されているRPC相互作用のアプローチは
、GWTアプリケーションでのユーザーサービスによる承認の記事で少し説明された単純なRPC呼び出しの役割を損なうものではありません。 場合によっては、プロジェクトでサーバー側に最大2つの呼び出しが1つある場合、コードを複雑にするだけであるため、アクション、結果、一部のGuiceなどの庭から庭をブロックすることはほとんど意味がありません。 一方、「正しい」OOPコードの構築方法を使用すると、構造化、可読性が向上し、_利益が増大します_。
さらに、サーバー側にJavaを通常含まないGWTプロジェクトをいくつか知っています。 そのため、このようなサーバー実装では、JSONやXMLなどの一般的なメッセージング形式を使用するのが自然です。 しかし、それは別の話です...
建設的な批判、願い、そしてもちろん質問を楽しみにしています!
ありがとう