SpringFrameworkの整理、個人的考察




SE4年目 Spring Frameworkにはなんとなく使用していたがどういった点で有利なのか漠然としか理解していない。
そろそろ知識を体系的に整理し、本当に便利なものなのか(らしいけど)1つ1つの機能を明らかにしていく + 個人的考察


参考にするHPとしては複数見ていますが(本買っていない)、主にTECHSCOREサンのHPを参考にさせていただく。
あと必要に応じて公式HP(英語)も見たり見なかったり。



1.目次

ToDo まだ全体像が見えていないので見えてから書く

2.概要



Rod Johnson氏の著書「Expert One-on-One J2EE Design and Development」の中で使用されたコードを元にした
オープンソースJava/J2EEアプリケーションフレームワークです。SpringはDIコンテナと呼ばれる Beanコンテナをコア
モジュールとし、下図のように7つのモジュールからなります。


らしいです。私は、読んだことはありません。
Springが本当にすばらしいものだと理解できたら、読むかも?


では下にその7つのモジュールはといいますと


ToDo 概要では、どんなことができて、なぜいいものなのかを噛み砕いて簡潔に書く。ここで
すべてを理解してもらう感じではない

2−1.Springの主な機能(モジュール別)


スプリングは機能別に以下の7つのモジュールに分かれています。


Coreモジュール


Core モジュールは Spring の根幹をなすモジュールであり、オブジェクトの生成・登録、オブジェクト間の関連づけを行う
Bean コンテナの機能を提供します。このモジュールにより、必要なオブジェクトの生成および初期化を一元化できるだけでなく、
プログラムから Singleton の実装とオブジェクト間の依存関係の実装を排除することができます。

このモジュールが、Coreだけあってもっともメインの機能を提供しているようだ。簡単に言うと
オブジェクト間の結合度を疎結合にして、依存性を外部から与えてあげるらしい。
この説明だけでは、よくわからないので後に実際に利用し有効性を検討。

Contextモジュール


JNDI のような方法で JavaBeans にアクセスする方法を提供します。
Context モジュールの1つである org.springframework.context は Core モジュールである
org.springframework.beans パッケージからその機能を継承していますが、
サーブレットコンテナによるリソースバンドルやイベント伝播等のテキストメッセージングのサポートが追加されています。

あまりよくわからないが、Coreモジュールが作成したJavaBeansに簡単にアクセスする手段を提供しているのか?


DAOモジュール


JDBC 抽象化レイヤを提供します。このモジュールを使用することで開発者が
Connection オブジェクトの取得・破棄、Statement オブジェクトの生成等を行う必要がなくなります。

DBアクセス周りの絶対にやらなくてはならないことをやってくれるようだ。


ORMモジュール


JDO, Hibernate, iBatis 等の object/relational mapping API を統合するレイヤを提供します。
ORM モジュールを使用することで O/R マッパを Spring の他の機能と組み合わせて使用できるようになります。

O/Rマッピングフレームワークと連携するらしい。具体的にどう組み合わさるのかイメージがわかない。
後に実際に利用し有効性を検討。


AOPモジュール


AOP Alliance と互換性のあるアスペクト指向プログラミングのフレームワークを提供します。
AOP モジュールを使用することで、たとえば session のチェック等のオブジェクトの責務外の
ロジックを明示的に記述することなく、宣言的に追加することができます。

デザインパターンのフィルターパターンの実装のような感じだろうか


Webモジュール


基本的な Web 指向の統合機能を提供します。Spring を WebWork や Struts と一緒に使用する場合は、
このモジュールが統合を行います。

Strutsとの絡みをやっていく


Web MVCモジュール


Web アプリケーション用の Model-View-Controller フレームワークを提供します。
Spring の MVC モジュールの特徴としてユーザが入力したデータを
JavaBeans として取得できることや、入力されたデータの検証処理が MVC モジュールから切り離されていることが挙げられます。

MVCモデルはわかるのだが、実際にどんなフレームワークを提供しているのだろう?

ここに書いてある説明では理解できないことも多々あるため、実際にすべてのモジュールを使用してみる。



3.DI: Dependency Injection


おそらくたぶんSpringFrameworkのメインの機能であろう、Dependency Injectionについて
この機能の有効性を詳しく説明できるなら、Springの根幹を抑えたと言ってもいい。     と思う。



Dependency Injection (依存性注入) とは、あるクラスが別のクラスをインスタンス変数に持つなどして利用 (依存)
している場合に、インスタンス変数の設定 (依存性の解決) をクラス内で行うのではなく、
外部から設定 (注入) するという考え方です。Spring の場合は、その外部が Core モジュールである DI コンテナになります。

以下にその概念を図にしてみた。他クラスの機能を使用したい場合に、そのクラスからnewして使用するのではなく、
使用する側のクラスにインターフェイルの宣言と、setterを記述しておくと、DIコンテナがセットしてくれる。(DIコンテナ用設定ファイル
の記述が必要)


そうすると、依存性が小さくなるらしい。なんで?使用する側がインターフェースを宣言しておいて、newしても同じじゃないのか?
直接newするのと、DIが設定してくれるのはどういう面で異なる?


3−1.DIのメリット

  • 強制的に疎結合となり、コンポーネント化が促進される。
  • テストが容易になる。(コンテナによって依存しているクラスがセットされるため、ダミーに置き換えるのが容易)
ToDo なぜDIがすばらしいものなのか具体例を用いて書く

3−2.実際に使ってみる

実際にSpringのDIを使用してみる。下図は今から使用するクラスたちの概念図。Mainから、ManオブジェクトをDIコンテナを
利用し、取得するの図。Manオブジェクトは、Houseオブジェクトをフィールドに持っている。DIコンテナはManとHouseの
依存性を設定しMainに返す。



下に作成したソースを載せる。実際にやってみて先ほどの疑問がわかったような気がする。ソース内でインジェクションせず
newしてしまうとそのクラスではないクラスを使用したい場合に、ソースを書き換えなくてはならない。
コードが大量になった場合手間になるのかな(設定ファイルでも手間のような気がするが)

beans.xml



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<bean id="man" class="jp.co.jjjjpppp.springtest.impl.Man" >
<property name="houseMaker"><ref bean="oobayashi"/></property>
</bean>

<bean id="oobayashi" class="jp.co.jjjjpppp.springtest.impl.Oobayashigumi" />

</beans>

Man.java


package jp.co.jjjjpppp.springtest.impl;

import jp.co.jjjjpppp.springtest.HouseMaker;

public class Man {

/** houseMaker */
private HouseMaker houseMaker;

/**
* 家を買う
*/
public void buyHouse(){
houseMaker.createHouse();
}

/**
* @param houseMaker the houseMaker to set
*/
public void setHouseMaker(HouseMaker houseMaker) {
this.houseMaker = houseMaker;
}
}


HouseMaker.java


package jp.co.jjjjpppp.springtest;


public interface HouseMaker {

/**
* 家を建設する。
*/
public void createHouse();
}


Oobayashigumi.java

package jp.co.jjjjpppp.springtest.impl;

import jp.co.jjjjpppp.springtest.HouseMaker;

public class Oobayashigumi implements HouseMaker {

/**
* @see jp.co.jjjjpppp.springtest.HouseMaker#createHouse()
*/
@Override
public void createHouse() {
System.out.println("六本木ヒルズを建築します。");

}

}


Main.java


package jp.co.jjjjpppp.springtest.impl;

import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;


public class Main {

/**
* @param args
*/
public static void main(String[] args) {

ClassPathResource res = new ClassPathResource(
"configerFiles/beans.xml");

XmlBeanFactory beanFactory = new XmlBeanFactory(res);

Man man = (Man) beanFactory.getBean("man");

man.buyHouse();

}

}

3−3.Listなどのプロパティのインジェクション


BeanFactory で管理する JavaBeans のプロパティに java.util パッケージの List, Map,
Set, Properties をセットしたいことがあるかもしれません。
そのような場合に対応するために Bean 定義ファイルの property 要素、
constructor-arg 要素には java.util パッケージの List, Map, Set, Properties にそれぞれ対応する
list, map, set, props 要素を指定することができます。 list 要素は List だけでなく配列のプロパティにも使用可能です。

みたいですね、例もあったのでそのまま載せます。

ToDo 暇なときに実際にためす


<bean id="exampleBean" class="com.techscore.spring.di.ExampleBean">
<property name="list">
<list>
<value>list 1</value>
<value>list 2</value>
</list>
</property>
<property name="map">
<map>
<entry key="mapKey1">
<value>map 1</value>
</entry>
<entry key="mapKey2">
<value>map 2</value>
</entry>
</map>
</property>
<property name="set">
<set>
<value>set 1</value>
<value>set 2</value>
</set>
</property>
<property name="properties">
<props>
<prop key="propKey1">prop 1</prop>
<prop key="propKey2">prop 2</prop>
</props>
</property>
</bean>


List を使用するには list 要素の子要素で List に追加するオブジェクトを指定します。
Map を使用するには map 要素の子要素に entry 要素を指定し、その key 属性でオブジェ
クトを登録するキーを指定します。登録するオブジェクトは entry 要素の子要素で指定します。
Set を使用するには set 要素の子要素で Set に追加するオブジェクトを指定します。
Properties を使用するには props 要素の子要素に prop 要素を指定し、その key 属性でプ
ロパティリストのキーを、テキスト値でプロパティを指定します。


List の具象クラスは java.util.ArrayList、Map の具象クラスは java.util.LinkedHashMap、
Set の具象クラスは java.util.HashSet です。また、list, entry, set 要素の子要素には以下
の要素を指定することが可能です。


  1. bean
  2. ref
  3. list
  4. set
  5. map
  6. value
  7. null




例を見てみると、プロパティの設定にvalueタグのみ使用している
この場合valueタグ内に記述した文字がStringオブジェクトとして設定される。
他のオブジェクトの型を設定したい場合どうするのか?試してみる。変えたソースは以下の2つ


beans.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>

<bean id="man" class="jp.co.jjjjpppp.springtest.impl.Man">
<property name="houseMaker">
<ref bean="oobayashi"/>
</property>
</bean>

<bean id="CC1" class="jp.co.jjjjpppp.springtest.impl.ConstructionCompany">
<property name="companyName">
<value>習志野建設</value>
</property>
<property name="money">
<value>2000万</value>
</property>
<property name="stuff">
<value>30人</value>
</property>
<property name="reration">
<value>下請け</value>
</property>
</bean>

<bean id="CC2" class="jp.co.jjjjpppp.springtest.impl.ConstructionCompany">
<property name="companyName">
<value>国土交通省</value>
</property>
<property name="money">
<value>20兆円</value>
</property>
<property name="stuff">
<value>4000人</value>
</property>
<property name="reration">
<value>施工主</value>
</property>
</bean>

<bean id="oobayashi" class="jp.co.jjjjpppp.springtest.impl.Oobayashigumi">
<property name="subcontractList">
<list>
<ref bean="CC1" />
<ref bean="CC2" />
</list>
</property>
<property name="stakesHolderMap">
<map>
<entry key="main1">
<ref bean="CC1" />
</entry>
<entry key="main2">
<ref bean="CC2" />
</entry>
</map>
</property>
<property name="methodProperties">
<props>
<prop key="main1">
昔ながらの方法
</prop>
<prop key="main2">
骨組み重視
</prop>
</props>
</property>

</bean>

</beans>

Oobayashigumi.java


/**
* SpringFrameworkTest 2008/07/07
*/
package jp.co.jjjjpppp.springtest.impl;

import java.util.List;
import java.util.Map;
import java.util.Properties;

import jp.co.jjjjpppp.springtest.HouseMaker;

/**
* @author
*
*/
public class Oobayashigumi implements HouseMaker {

/** 協力建築会社 */
public List subcontractList;

/** 建築の利害関係者 */
public Map stakesHolderMap;

/** 建築の工法 */
public Properties methodProperties;

/**
* @see jp.co.jjjjpppp.springtest.HouseMaker#createHouse()
*/
@Override
public void createHouse() {
System.out.println("六本木ヒルズを建築します。");

System.out.println("★以下の会社に協力してもらいます。");
for (ConstructionCompany constructionCompany : subcontractList) {
System.out.println(constructionCompany.getCompanyName()
+ constructionCompany.getStuff()
+ constructionCompany.getMoney());
}
System.out.println("★以下のステークスホルダーが存在します。");
ConstructionCompany cc1 = (ConstructionCompany) stakesHolderMap
.get("main1");
System.out.println(cc1.getReration());

System.out.println("★以下の工法で建築します");

System.out.println(methodProperties.get("main2"));
}

/**
* @param stakesHolderMap
* the stakesHolderMap to set
*/
public void setStakesHolderMap(Map stakesHolderMap) {
this.stakesHolderMap = stakesHolderMap;
}

/**
* @param methodProperties
* the methodProperties to set
*/
public void setMethodProperties(Properties methodProperties) {
this.methodProperties = methodProperties;
}

/**
* @param subcontractList
* the subcontractList to set
*/
public void setSubcontractList(List subcontractList) {
this.subcontractList = subcontractList;
}

}



ConstructionCompany.java



package jp.co.jjjjpppp.springtest.impl;

public class ConstructionCompany {

private String companyName;

private String money;

private String stuff;

private String reration;

/**
* @return the reration
*/
public String getReration() {
return reration;
}

/**
* @param reration the reration to set
*/
public void setReration(String reration) {
this.reration = reration;
}

/**
* @return the stuff
*/
public String getStuff() {
return stuff;
}

/**
* @param stuff the stuff to set
*/
public void setStuff(String stuff) {
this.stuff = stuff;
}

/**
* @return the companyName
*/
public String getCompanyName() {
return companyName;
}

/**
* @param companyName the companyName to set
*/
public void setCompanyName(String companyName) {
this.companyName = companyName;
}

/**
* @return the money
*/
public String getMoney() {
return money;
}

/**
* @param money the money to set
*/
public void setMoney(String money) {
this.money = money;
}

}


コードを実行すると以下の出力が得られる。


出力


六本木ヒルズを建築します。
★以下の会社に協力してもらいます。
習志野建設30人2000万
国土交通省4000人20兆円
★以下のステークスホルダーが存在します。
下請け
★以下の工法で建築します
骨組み重視
2008/07/09 11:10:50 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
情報: Loading XML bean definitions from class path resource [configerFiles/beans.xml]


続きはHTMLで書くと時間がかかるので、はてなBlogへ書き込む。