レベルエンター山本大のブログ

面白いプログラミング教育を若い人たちに

BLOCKVROCKリファレンス目次はこちら

Guice & Servlet

GuiceServletの連携というのをやってみます。
今回試すのは、Guiceで管理するオブジェクトに「RequestScoped」「SessionScoped」というスコープを設定することです。
Guiceのスコープの範囲で、「リクエスト」と「セッション」が使えるようになります。
GuiceServletの連携では、この他にも幾つかの機能が得られるようです。(それはまた後ほど)

資料がないのでAPIとにらめっこでやります。


Tomcat 5.5を使いました。
まずは以下のライブラリを利用します。

ディレクトリ構成

webapps
Guice
 ├WEB-INF
 │├classes
 ││└(クラス生成先)
 │├src
 ││└(作成ソースファイル)
 │└lib
 │ ├guice-servlet-1.0.jar
 │ └guice-1.0.jar
 └Hello.jsp

作成するファイル 役割
net.kronos_jp.guice.servlet.HelloService インジェクトされるサービスオブジェクト、Guiceアノテーションでリクエストスコープのオブジェクトに設定します。
net.kronos_jp.guice.servlet.HelloWorld サーブレットを継承。Guiceから受け取ったHelloServiceのフィールドを書き換えます。その後Hello.jspへ転送します。
Hello.jsp HelloWorldサーブレットから転送を受け付けてGuiceからHelloServiceオブジェクトを取得。メッセージを表示します。
web.xml Guiceが提供するFilterやサーブレットクラスを登録します。

web.xml

Guiceが提供するフィルター「GuiceFilter」を全てのリクエストに適用します。
このフィルターによって、Guice&Servletで機能する様々な仕組みが提供されます。
その他はHelloWorldサーブレットを登録してます。

<?xml version="1.0" encoding="SHIFT_JIS"?>
<web-app version="2.4">
	<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>

	<servlet>
		<servlet-name>helloworld</servlet-name>
		<servlet-class>net.kronos_jp.servlet.HelloWorld</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>helloworld</servlet-name>
		<url-pattern>/hello/*</url-pattern>
	</servlet-mapping>
</web-app>

HelloService.java

HelloServiceは、Guiceで管理するオブジェクトです。
@RequestScopedアノテーションが設定されていることで、リクエストスコープの間1つのインスタンスが使われます。
このほか、@SessionScopedアノテーションも提供されています。

package net.kronos_jp.servlet;

import com.google.inject.servlet.RequestScoped;

@RequestScoped
public class HelloService {
	private String message;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

HelloWorld.java

HelloWorld サーブレットは、GuiceからHelloServiceオブジェクトのインスタンスを取得し、
フィールド値を書き換えてHello.jspへ転送する役割を持っています。

package net.kronos_jp.servlet;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import com.google.inject.*;
import com.google.inject.servlet.ServletModule;

public class HelloWorld extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		Injector injector = Guice.createInjector(new ServletModule());
		
		HelloService hello = injector.getInstance(HelloService.class);
		
		hello.setMessage("Hello Hello!");
		
		request.getRequestDispatcher("Hello.jsp").forward(request,response);
	}
}

Hello.jsp

HelloServiceがリクエストスコープのオブジェクトであるため、GuiceからHelloServiceオブジェクトを取得すると
先ほどHelloWorldサーブレットでフィールド値を書き換えたインスタンスが取得出来ます。
HelloServiceのインスタンスを取得してメッセージを表示しています。

<%@page import="net.kronos_jp.servlet.HelloService" %>
<%@page import="com.google.inject.Guice" %>
<%@page import="com.google.inject.servlet.ServletModule" %>
<HTML>
<BODY>
<%
HelloService hello = com.google.inject.Guice.createInjector(new ServletModule()).getInstance(HelloService.class);
%>
<B><%= hello.getMessage() %></B>

</BODY>
</HTML>

実行結果

http://localhost:8080/Guice/helloでHelloWorldサーブレットを起動すると「Hello Hello!」と表示されました。

リクエストスコープであることを確認したいならば以下のように試してみることができます。
HelloWorldサーブレットを実行した後で、同じブラウザを使ってhttp://localhost:8080/Guice/Hello.jspを直接実行したとしても、オブジェクトは生存しておらず「null」と表示されます。(別リクエストとなるため)

またstaticフィールドを使ったシングルトンでもないので、ブラウザのインスタンスをもう1つ起動して「HelloWorldサーブレット」と「Hello.jsp」を、それぞれ別々に実行したとしても、Hello.jspに直接アクセスしたほうでは、elloWorldサーブレットの影響を受けず、やはり「null」と表示されます。


@SessionScopedに切り替えるのも、1つの箇所を書き換えるだけです。
この場合、一度HelloWorldサーブレットを実行したブラウザでは「Hello.jsp」を直接表示してもメッセージが表示されます。