(*)
一般的には...
話を発散させたくないので、J2EEに話を限定します。
扱う範囲
扱わない範囲
以下をインストール、セットアップしてください
Springは依存関係が多いので、-with-dependenciesのついた配布を使うのがお薦めです。
DIについて (2004年1月)
用語などは基本的にこれに従います。
定義は曖昧です...
理念としては、「再利用可能な部品のような」ソフトウェア
「境界を意識して」、「依存度(*)を意識して」、ソフトウェアをモジュール化していれば、結果的にコンポーネントになります(たぶん)。
狭義では、バイナリ/言語に対する非依存をコンポーネントの要件にする宗派もあります。Javaの世界に閉じこもれば無関係です。
(*)依存度をシンプルに。ほとんどの場合、依存度を小さくすることでシンプルさは達成できますが、目指すべきはシンプルさです(私見)。
DIという用語が現れる以前に、IoC(Inversion of Control)と呼ぶ人もいました。
IoCという用語は、いわゆるイベントドリブンに見られる「Hollywood Principle (don't call us, we'll call you.)」と区別がつきません
=> わざわざ新しい用語を持ち出すまでも無い
(*)リフレクションを活用して、オブジェクトの生成と構成を行います
依存を外部化 => POJO(Plain Old Java Object) => 単体テストしやすい! => Javaは素晴らしい!
...そもそも依存度を小さくモジュール化するのはJava以前の常識なので、Javaの世界が狂っていただけ
しかし、DIは素晴らしいと思います。まさに「家貧しくて孝子顕る」です
public class MyFoo {
private List<String> list = new ArrayList<String>();
...
}
この"new ArrayList<String>()"をソースコードから消して、外部(普通はXML設定ファイル)に追い出します。
DIを知らない、普通の感覚でソースを見ると、本来あるべき"new Foo"が見つからなくて不思議な気分になります。
そもそも
<< ArrayList<String> list = new ArrayList<String>(); //bad >> List<String> list = new ArrayList<String>(); //good
後者で書く気持ちは、"listはListインターフェースとして振る舞う何かを指す"の意志表示なので、変数の型からArrayListを追い出しても、"new ArrayList"がソースに残っていたら負けです。
=> だからDIを使います。
個人的にはこの分類にあまり意味を感じませんが(*)、3つのタイプにDIを分類します
(*)個人的には、「Constructor Injection」派です
クラスを用途で2分してみます
-- 実体(状態遷移)の表現を役割の主とするクラス
-- サービス提供を役割の主とするクラス
インターフェースに対してプログラミングして具象クラスから疎結合であるべきなのは後者。
=> DIを使うのは「サービス的」なクラス (がbetterプラクティス)
<?xml version="1.0"?>
<project name="sample" basedir="." default="build">
<property name="src.dir" location="."/>
<property name="build.dir" location="."/>
<property name="spring.dir" value="/opt/java/lib/spring-framework-1.2.6"/>
<path id="master-classpath">
<fileset dir="${spring.dir}/dist">
<include name="**/*.jar"/>
</fileset>
<fileset dir="${spring.dir}/lib">
<include name="**/*.jar"/>
</fileset>
<fileset dir="/usr/share/java">
<include name="*.jar"/>
</fileset>
</path>
<target name="build" description="Compile main source tree java files">
<javac srcdir="${src.dir}" destdir="${build.dir}" target="1.5" debug="true" deprecation="true">
<classpath refid="master-classpath"/>
<!--compilerarg value="-Xlint:all"/-->
</javac>
</target>
<target name="run" depends="build">
<input message="Class to run?" addproperty="class_name"/>
<java classname="${class_name}" classpath=".">
<classpath refid="master-classpath"/>
</java>
</target>
</project>
<form action="/foo" method="GET or POST"> <input type="text" name="bar"/> <input type="submit" name="ok"/> </form>
GETの場合
=> サーバ GET /foo?bar=value HTTP/1.1 色々...(ヘッダの最後が空行) <= サーバ レスポンス
POSTの場合
=> サーバ POST /foo HTTP/1.1 色々...(ヘッダの最後が空行) bar=value <= サーバ レスポンス
GETでもPOSTでも、サーブレット(J2EEのAPI)でプログラミングしていれば、
-- パラメータ名: bar -- パラメータ値: value
注意1
注意2
こういう場合、パラメータふたつで、両方ともパラメータ名は"bar"
理屈上はすべてPOSTでもアプリは作れます...
が、格好わるいので使い分けるべきです。
HTTPの理念的には
例外がふたつあります。 たとえリソースを書き換えないリクエストでも次のふたつはPOSTを使います。
GETを積極的に使うべき場面
基本的なアイディア
J2EEの世界では、クッキーもしくは(クッキーが使えない環境のために)URLパラメータでユーザ識別をします。
=> セッション管理
セッション管理の文脈では、J2EEがクッキーの存在を隠蔽しているので、アプリ開発者は気にする必要はありません。
keyword: HttpSession
クラシックなWebサーバ
色々、変遷...
サーブレットエンジン
該当するクラスのインスタンスは、リクエストを入力として受け取り、出力としてレスポンスを返すように実装します(J2EEのAPI利用)。
(*)狭義の「サーブレットクラス」
web.xmlファイルで行います(Servletエンジンのレベル)
web.xmlファイルの見方
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
servlet spec. 11.2. Specification of Mapping
- beginning with '/' and ending with '/*' => パスマッピング - beginning with '*.' => 拡張子マッピング - only '/' => デフォルトマッピング - exact match
<!-- jspを処理するクラスとURLパターンのマッピング例 --> <servlet> <servlet-name>jsp</servlet-name> <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>jsp</servlet-name> <url-pattern>*.jsp</url-pattern> </servlet-mapping>
理屈上は、
で、サーブレット上のWebアプリ開発はできます。
全部jspにして*.jspのマッピングに任せたり、(Tomcatの)invokerクラスで*.classを直接叩くURLにすると、深く考えずマッピングができます。
が、ちょっと格好悪いのと、気を付けないと、次のような弊害がでてきます。
=> MVCアーキテクチャ(役割を明確化してモジュール化)
コントローラを明確化します
この役割を分離します
宗派
Springは
web.xml(Tomcatの世界)
<servlet> <servlet-name>appname</servlet-name> <!-- appnameはアプリ名 --> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>appname</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
appname-servlet.xml (アプリ名-servlet.xmlはSpringが読むファイル)
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="urlMap">
<map>
<entry key="/login"><ref local="authorizeController"/></entry>
略
結果的に、
コントローラという名前で、微妙に異なる役割の2種類のクラスが存在します
前者を「ディスパッチャーコントローラ」「コントローラ of コントローラ」と呼んで、後者と区別します。
多くの場合、「ディスパッチコントローラ」はWebフレームワークが用意しているので、アプリ開発者が実装するのは、後者の「(狭義の)コントローラクラス」です。
モデルという用語の宗派
Springは
Springのモデルは
なので、結果的に
ことになります。
(*)「加工しない」ケースも含みます。つまり出力データそのものの場合です。
理屈上は、
となります。
この時、コントローラは、
ちょっと知りすぎ(*)なので、コントローラとビジネスロジックの間に「サービス層」を作るのが推奨です
(*)依存度をシンプルに、の原則
で、こんなにモデルを偏執狂的に裏側から切り離すことに意味はあるのか?
アリエルでは、ここまで偏執狂的にやっていません。
一方、ビューのコードは、変更しやすく保ちたいので、
ことは重要です(ビューの依存度をシンプルに)
モデルを通じて、裏側の事情がだだ洩れにならないようにはしたいものです。
HTMLのフォームが中心となるアーキテクチャ(に見える)
いくつかの必要条件を満たしたクラスは、Beanクラスになりえます。 厳しい必要条件ではないので、どちらかと言うと、利用側の意図で、「これはBeanかどうか」が決まります。
# EJB(Enterprise Java Beans)は、更に(主に分散処理を念頭にした)必要条件が追加した別物です。
Beanクラスは、プロパティをprivateに持ち、setterとgetterのアクセサメソッドを提供します。
# ここから厭味。
Webアプリに限定すれば、ビューの定義は
です。
多くはHTML生成ですが、AJAX対応などでXML生成やJSON生成だったりします。
Javaの世界だけでもビュー技術はたくさんありますが、保守本流はJSPです 保守的なので、以下、JSPに話を限定します
# Spring自体はJSPへの依存を避けて、異なるビュー技術をサポートしています
JSPの動作イメージ
つまり、サーブレットの.javaファイルを書くことと、.jspファイルを書くことは記述の差でしかありません
# 感覚的には、
# サーブレットクラスの場合、Javaのコードの中にHTML出力コードが紛れ込む # JSPファイルの場合、HTMLの中にJavaコードが紛れ込む
=> JSPの中に書かれたロジックを「スクリプトレット」と呼んでバカにするのがJava流(私見)
=> ストレス発散のために、せめてPHPやASPぐらいはバカにするのがJava流(私見)
(*)これで完結するスタイルは、いわば、URLにマッピングしたクラスの中でリクエスト解析、レスポンス生成を全部やってしまうパターンのJSP版です
# でも、PHPでもやっているし、簡単な動作確認なら、これでもいいかも (私見)
説明の前にServletのお約束
WEB-INFディレクトリの下は、*外部から直接URLで叩けません* (叩かせてはいけないファイルを置きます)
jspファイルはWEB-INFの外に置けば、外部からURLで直接叩けます(web.xmlで*.jspとのマッピングをしていれば)
Springの流儀は
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass"><value>org.springframework.web.servlet.view.JstlView</value></property> <property name="prefix"><value>/WEB-INF/jsp/</value></property> <property name="suffix"><value>.jsp</value></property> </bean>
概要
-- 名前で識別される複数のModelオブジェクト(通常、BeanオブジェクトもしくはMap) -- ビュー名
-- モデル名.プロパティ名 (Bean) -- モデル名.キー (Map)
実践