Deletions are marked like this. | Additions are marked like this. |
Line 13: | Line 13: |
したがって、少なくともWicketのページ及びその中のコンポーネント等では、DIではなくサービスロケータを使用するのがベストであると結論づけた(「[[guice/ServiceLocator|Injectorをサービスロケータとして使う]]も参照のこと」)。 | したがって、少なくともWicketのページ及びその中のコンポーネント等では、DIではなくサービスロケータを使用するのがベストであると結論づけた(「[[guice/ServiceLocator|Injectorをサービスロケータとして使う]]」も参照のこと)。 |
GuiceFilterの利用
サーブレットフィルタとしてGuiceFilterを用いると、@SessionScopedや@RequestScopedのアノテーションが有効になる。 それらのスコープの制御は、Guiceがサーブレットからのリクエストをフックして行うからである。
Wicketでこの機能を利用するには、基本的には「サーブレット」に記述した通りのことを行えばよい。 ただし、これをやっても「インジェクションの方法」で取り上げた問題が解決するわけではない。 相変わらず以下の問題があるのでまとめておく。
- コンストラクタ注入されるオブジェクト以外はコンストラクタ中で使用することはできないが、一般にWicketのページクラスにはそのような特別なパラメータを指定できる余地がない。
- フィールド注入したとしても、Wicketのページは直列化されるため、それらのオブジェクトはSerizlizableでなければならず、大きいと直列化領域を圧迫する。なおかつ、直列化復帰後に(DB接続等の)機能を復旧できなければならない。
したがって、少なくともWicketのページ及びその中のコンポーネント等では、DIではなくサービスロケータを使用するのがベストであると結論づけた(「Injectorをサービスロケータとして使う」も参照のこと)。
その上で、@SessionScoped/@RequestScopedの恩恵にあずかることはできる。 サービスロケータからこれらのオブジェクトを取得すると、それぞれセッションで一意なオブジェクト、リクエストで一意なオブジェクトを取得することができるのである。
そしてむしろ、これらのスコープされたオブジェクトを使う場合にはオブジェクトそのものの注入は避けるべきである。 なぜなら、注入先のオブジェクトがリクエストやセッションを越えて生き残る場合には、誤ってそのスコープ外でスコープされたオブジェクトを使ってしまうというバグになりかねないからである。
したがって、これらのスコープされたオブジェクト使う場合は、必ずプロバイダあるいはサービスロケータから取得するのがベストと思われる。
Guice, Wicket, Jettyを使う例
以下では、Guice 2.0, Wicket 1.4.3, Jetty 7.0.1を利用している。 特にJettyはバージョンによって仕様がよく変更されているので注意・
Guiceは極力XMLを排除するように設計されている。ウェブとサーブレットでのGuiceFilterの利用例でもこれが貫かれている。 それでも他のサーブレットコンテナでは最低限のweb.xml記述が必要と思われるが、 Jettyでは完全にweb.xmlを排除することができる。
ほとんどのセットアップはInjectorの作成時に行えばよい。 仮にInjectorが得られたものとして、以下のコードでJettyを起動する。 以下のコードは、ほぼどんな場合でも同じになると思われる。
Server server = new Server(); Connector connector = new SocketConnector(); connector.setPort(8080); server.addConnector(connector); ServletContextHandler context = new ServletContextHandler(server, "/", ServletContextHandler.SESSIONS); context.addFilter(GuiceFilter.class, "/*", 0); context.addEventListener(new GuiceServletContextListener() { @Override protected Injector getInjector() { return injector; } }); context.addServlet(DefaultServlet.class, "/"); server.start(); server.join();
次に、Injectorの生成について見てみるが、その前にWicketFilterのサブクラスを作成し、 @Singletonを指定しておく。
import org.apache.wicket.protocol.http.*; import com.google.inject.*; @Singleton public class MyWicketFilter extends WicketFilter { }
Injectorに渡すモジュールとして、特別なServletModuleを使う。ここでは、拡張しやすいようにサブクラス化しておく。 他のフィルターがもしあればそれを先に指定できるように、configureFilters()を下位クラスでオーバライドできるようにする。
import java.util.*; import com.cm55.clman.web.*; import com.cm55.clman.web.lic.*; import com.cm55.third.wicket.*; import com.google.inject.servlet.*; public class MyServletModule extends ServletModule { protected final void configureServlets() { // 他のフィルタ configureFilters(); // WicketFilterの設定 Map<String, String> params = new HashMap<String, String>(); params.put("applicationClassName", MyApplication.class.getCanonicalName()); filter("/*").through(MyWicketFilter.class, params); } protected void configureFilters() { } }
そしてインジェクタを作成するのだが、通常のモジュールと共にMyServletModuleを指定する。
Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { // 通常のバインドがあればここで指定 } }, new MyServletModule() { protected void configureFilters() { // 他のフィルタが必要であればここで指定 } } );
ServletModuleは、通常のモジュールと同様にいくつ指定しても構わないようである。 しかし、フィルタの順序は重要なので、できれば一つのServletModuleの中に適切な順番でフィルタを並べた方がよいだろう。