Spring Frameworkの理解が進まない今日この頃ですが、今回はSpring FrameworkのなかのDI(Dependency Injection)についてです。
その前に、前回、『MySampleWebApp』というプロジェクトをSTSで作成しました。
今回、javaファイルをまだ作成していないのでjavaのプログラムを実行する
public static void main(String[] args){ }
がないのに、『Pivotal tc Server Developer Edition v3.1』というサーバを起動し、『http://localhost:8080/MySampleWebApp』にアクセスしたところ、javaプログラムが実行され、index.jspが表示できていました。
これは、『Pivotal tc Server Developer Edition v3.1』というサーバがアプリケーションサーバとWebサーバの両方の機能を兼ね備え、そのなかのServletコンテナクラスで『public static void main(String[] args){ }』が実行されているため、index.jspの中のjavaプログラムの部分が実行できているのではないかと思います。
MVCアーキテクチャにおけるJSP, Java Servlet, JavaBeansの位置づけの図
JSP(JavaServer Pages)
JavaServer Pages (JSP) は、HTML内にJavaのコードを埋め込んでおき、Webサーバで動的にWebページを生成してクライアントに返す技術。
HTMLの中でデザイン部分とプログラム部分を分けて書くためにある程度までウェブデザイナの負担を減らすこともできる。また、静的な出力が多い場合に適している。類似技術としてPHP、ASP、ASP.NETなどがある。
クライアントからのJSPの実行がリクエストされると、アプリケーションサーバのサーブレットコンテナはJSPソースファイルをサーブレットのソースコードに変換する。そしてさらにそのソースコードをその場でコンパイルして実行し、結果をクライアントに返信する。このため、最初はコンパイルの時間がかかるが、いちどコンパイルが実行されると2回目以降は必要なくなるため、結果としてアクセス速度が早くなる。
Java Servletを自動生成して動作している。
Java Servlet
サーバ上でウェブページなどを動的に生成したりデータ処理を行うために、Javaで作成されたプログラム及びその仕様である。単にサーブレットと呼ばれることが多い。Java EEの一機能という位置づけになっている。
同様の技術(すなわち対抗技術)としてはPerlなどを用いたCGI、PHPプログラムのプロセスをApache HTTP Server上で動かすことができるmod_phpなどのモジュール、マイクロソフトが提供するASPなどがある。
CGIがクライアントのリクエストのたびに新しいプロセスを起動するのに対して、サーブレットはメモリに常駐して、リクエストのたびにプロセスより軽量なスレッドを起動するので、効率がよい。
また、サーブレットはJavaで書かれているのでさまざまなプラットフォームで使うことができる。
当初、JavaはAppletなどのクライアント側でJavaプログラムを稼動させるクライアントサイドの技術として注目を集めていた。
しかし、サーブレットの登場以降、サーバ側でJavaプログラムを稼動させる形態が急速に普及した。こうしたサーバ側でJavaプログラムを稼動させる形態をサーバサイドJavaと呼ぶ。
Javaは最初からサーバーサイド言語ってわけではなかったっていうのは衝撃でした。
DI(Dependency Injection)とは?
だいぶ話が脱線しましたが、Spring Frameworkでは、DIがかなり重要みたいです。
じゃあ、DIってなんなんすか?ってことですが、日本語で『依存性の注入』というらしいです。
依存性の注入(DI: Dependency injection)
コンポーネント間の依存関係をプログラムのソースコードから排除し、外部の設定ファイルなどで注入できるようにするソフトウェアパターンである。
コンポーネント間の関係はインタフェースを用いて記述し、具体的なコンポーネントを指定しない。
プログラムに依存性を注入する方法(DIの種類)としては、以下のような手法が存在する。
Dependency injectionという用語を作成したのはソフトウェア開発者のマーティン・ファウラーである。
類似の概念としてそれ以前から制御の反転 (IoC) と呼ばれるアイデアが存在していたが、それを整理・範囲を限定することでDIが生み出された。
現在では代表的なDIコンテナとして知られるSpring Frameworkも、誕生当初はDIではなくIoCという表現を用いていた。流石はWikipediaさん...、まったく分からんっす!
『Spring Framework4 (著:掌田津耶乃)』の説明によると、DI(依存性注入)は、「オブジェクトA」と「オブジェクトB」というコンポーネントで「固有の設定情報」を外部ファイル化するってことみたいです。
DI(Dependency Injection)のイメージ図
DIコンテナ(IoCコンテナ)のイメージ図
Spring Frameworkでは、DIコンテナ(IoCコンテナ)として実装されたクラス
- ApplicationContextクラス
- BeanFactoryクラス
などを利用してコンポーネント側の機能を呼び出していくようです。
SpringでDIコンテナを使うことの特徴
⇩ 詳しくは下記サイトへ
クラスのオブジェクト(インスタンス化)を毎回作っていると、メモリの消費などが膨大になって、結果的に処理が遅くなってしまうのを、DIコンテナ利用で回避できるようです、たぶん。
SpringでDIする方法
2パターンあるようです。
- アノテーションを使う
- Bean定義ファイルを使う
現在は、アノテーションを使う方法が主流のようです。
ですが、前回作った『MySampleWebApp』プロジェクトではBean定義ファイルを使っている(『Spring Framework4 (著:掌田津耶乃)』)ようなので、Bean定義ファイルでDIしていくことにします。
SpringのWebプロジェクトでBean定義ファイルでDIしてみる
STSを起動します。
src/main/resources/spring/application-config.xmlというファイルが、SpringでDIで利用する情報をまとめた「Bean定義ファイル」のようです。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Uncomment and add your base-package here: <context:component-scan base-package="org.springframework.samples.service"/> --> </beans>
ここに、Beanクラス(javaファイル)を管理するための記述をしていくようです。
Beanクラスを作成
『MySampleWebApp』プロジェクトのsrc/main/javaに、MyBean.javaというクラスを作成します。
『ファイル』>『新規』>『クラス』を選択します。
『パッケージ名(K):』と『名前(M):』を適当につけ、『完了(F)』を選択します。
Beanクラス(MyBean.java)が作成されました。
package jp.suzuki.spring.websample; public class MyBean { }
作成したBeanクラスに、いろいろ足していきます。
package jp.suzuki.spring.websample; import java.util.ArrayList; import java.util.List; public class MyBean { private Listmessages = new ArrayList (); public MyBean() { super(); messages.add("This is Bean sample."); } public void addMessage(String message) { messages.add(message); } public String getMessage(int n) { return messages.get(n); } public void setMessage(int n, String message) { messages.set(n,message); } public List getMessage() { return messages; } public void setMessages(List messages) { this.messages = messages; } @Override public String toString() { String result = "MyBean [\n"; for(String message : messages) { result += "\t" + message + "\n"; } result += "]"; return result; } }
application-config.xmlに追記
Beanクラス(MyBean.java)の情報をBean定義(application-config.xml)ファイルに記述していきます。
<bean id="mybean1" class="jp.suzuki.spring.websample.MyBean" />
の一文を追加します。
※classは、作成したパッケージ名とBeanクラス名を『.』でつなげたものを指定。idは、後ほど作成するMySampleServlet.javaの中のgetBean()の引数で使います。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- Uncomment and add your base-package here: <context:component-scan base-package="org.springframework.samples.service"/> --> <bean id="mybean1" class="jp.suzuki.spring.websample.MyBean" /> </beans>
Servletクラスを作成
Beanクラス(MyBean.java)を処理するためのServletクラスを作成していきます。
『MySampleWebApp』プロジェクト内の『src/main/java』が選択された状態で、『ファイル』>『新規』>『その他』を選択します。
『Web』>『サーブレット』を選択し、『次へ』をクリックします。
『Javaパッケージ名』『クラス名』などを適当につけ、『次へ』をクリックします。
『URLマッピング』で『/MySampleServlet』を選択した状態で『編集』をクリックします。
『URLマッピング』の『パターン』に『/sample』と入力し、『OK』をクリックします。
『次へ』をクリックします。
『継承された抽象メソッド』『init』『doGet』『doPost』にチェックが入った状態で、『完了』をクリックします。
MySampleServlet.javaクラスが作成されました。
package jp.suzuki.spring.websample; import java.io.IOException; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Servlet implementation class MySampleServlet */ public class MySampleServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see Servlet#init(ServletConfig) */ public void init(ServletConfig config) throws ServletException { // TODO Auto-generated method stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub response.getWriter().append("Served at: ").append(request.getContextPath()); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
MySampleServlet.javaを編集していきます。
package jp.suzuki.spring.websample; import java.io.IOException; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Servlet implementation class MySampleServlet */ public class MySampleServlet extends HttpServlet { private static final long serialVersionUID = 1L; ApplicationContext app; /** * @see Servlet#init(ServletConfig) */ @Override public void init() throws ServletException { super.init(); app = new ClassPathXmlApplicationContext("/spring/application-config.xml"); } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { MyBean mybean1 = (MyBean)app.getBean("mybean1"); request.setAttribute("mybean",mybean1); request.getRequestDispatcher("/index.jsp").forward(request, response); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String message = request.getParameter("message"); MyBean mybean1 = (MyBean)app.getBean("mybean1"); mybean1.addMessage(message); response.sendRedirect("sample"); } }
web.xmlを確認
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>MySampleWebApp</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/application-config.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>dispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/mvc-config.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <servlet> <description></description> <display-name>MySampleServlet</display-name> <servlet-name>MySampleServlet</servlet-name> <servlet-class>jp.suzuki.spring.websample.MySampleServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MySampleServlet</servlet-name> <url-pattern>/sample</url-pattern> </servlet-mapping> </web-app>
STSのサーブレット作成機能を利用して、Servletクラスを作成すると作成したServletクラスの情報が自動でweb.xmlに記述されます。
index.jspを変更
<!DOCTYPE html> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <html> <head> <meta charset="utf-8"> <title>Welcome</title> </head> <body> <h1>Sample Page</h1> <pre>${mybean}</pre> <hr> <form> <input type="text" id="message" name="message"/> <input type="submit" value="add" /> </form> </body> </html>
サーバーを起動してアクセスしてみる
『Pivotal tc Server Developer Edition v3.1』を起動し、ブラウザで『http://localhost:8080/MySampleWebApp/sample』にアクセスすると変更したindex.jspの内容が表示されました。
今回はこのへんで。