オープンソースの中を

オープンソースの中を覗いて勉強になることがある。
Google guiceServlet を利用する時。
web.xml の、<servlet-mapping>を Javaで記述というと、そんなことして何の意味がある!と思うだろうが、
実際に実行すると意外な機能に気づく。

GuiceServletContextListener を継承したリスナを用意して、web.xml に以下のように
記述する。
    <listener>
        <listener-class>sample.labo.MyListener</listener-class>
    </listener>

<servlet-mapping>の記述を web.xml から排除する。

================ MyListener.java ================
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.servlet.GuiceServletContextListener;

public class MyListener extends GuiceServletContextListener{
   @Override
   protected Injector getInjector(){
      return Guice.createInjector(new MyServletModule());
   }
}
================ MyServletModule.java ================
import com.google.inject.servlet.ServletModule;

public class MyServletModule extends ServletModule{
   @Override
   protected void configureServlets(){
      serve("/aa/*").with(AaServlet.class);
      serve("/bb/*").with(BbServlet.class);
   }
}
これで、URI パターンに対する実行サーブレットを定義する
これだけを見ると単にJavaで定義するようになる効果しか見えないが、、、
サーブレットを実行時に、
上記の extends ServletModule の威力が発揮されて、各サーブレットから実行する
doGet や doPost で任意ユーザーインタフェースのDI生成したインスタンスで、
HttpServletRequest 等を自動的に注入できる。

================ AaServlet.java ================
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.inject.Injector;
import com.google.inject.Singleton;

// クラス宣言に、Singleton アノテーションは必須
@Singleton
public class AaServlet extends HttpServlet{
   private static final long serialVersionUID = 1L;
   private Injector injector;
   @Override
   public void init(ServletConfig config) throws ServletException{
      super.init(config);
      this.injector = (Injector)config.getServletContext()
                      .getAttribute("com.google.inject.Injector");

   }
   @Override
   protected void doGet(HttpServletRequest request,HttpServletResponse response)
   throws ServletException,IOException{
      Dispatcher dispatch = this.injector.getInstance(ASampleDoGet.class);
      dispatch.exec();
   }
   @Override
   protected void doPost(HttpServletRequest request,HttpServletResponse response)
   throws ServletException,IOException{
      //
   }
}
↑↑↑Dispatcher という任意インターフェース←ASampleDoGet.class クラスDI生成させて実行
================ ASampleDoGet.java ================
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.google.inject.Inject;
import com.google.inject.servlet.RequestParameters;

public class ASampleDoGet implements Dispatcher{
   @Inject private HttpServletRequest request;
   @Inject private HttpServletResponse response;
   @Inject @RequestParameters private Map<String, String[]> params;
   @Inject private HttpSession session;


   @Override
   public void exec(){   // doGet で実行
      // private ローカル変数を利用できる。
   }
}
====================================================
これは、com.google.inject.servlet.GuiceServletContextListener のソースを覗くことで、
サーブレットコンテキストに Injector が、ServletContext#setAttribute で格納されてる
ことが判りユーザのサーブレットの public void init(ServletConfig config) で
サーブレットコンテキストより、extends ServletModule でリスナに作らせた
Injector を取得できるのである。
このInjector を利用することが、HttpServletRequest のバインド定義を
有効にさせている。