Powered by SmartDoc

Springフレームワークの紹介

2003/12
ロッドジョンソン著 金田忠士訳
http://www.theserverside.com/articles/article.tss?l=SpringFramework
今年の夏、Springフレームワークに関しての噂話を聞いたことがあるかもしれない。本稿では、Springが何をやり遂げようとしているか、そして私がどうのようにJ2EEアプリケーションを開発するのに役に立つと信じているのかを説明しようと思う。

目次

また他のフレームワーク?

ひょっとすると「もう他のフレームワークなんていらない」と考えているかもしれない。既に多くのオープンソースの(あるいはプロプライエタリな)J2EEフレームワークがあるのに、なぜわざわざこの記事を読まないといけないのか、あるいはSpringフレームワークをダウンロードしないといけないのか。

私は下記に挙げた2,3の理由でspringはユニークだと考えている。

Springさえあれば、プロジェクトに他のフレームワークを用意する必要性はない。Springは潜在的にワンストップであり、典型的なアプリケーションに関連するほとんどのインフラに用いることができる。もはや他のフレームワークは必要ない。

2003年2月から始まったばかりのオープンソースプロジェクトではあるが、Springには古い遺産がある。このオープンソースプロジェクトは2002年終わりの拙著「Expert One-on-One J2EE Designe and Development」に掲載の基盤コードから始まった。「Expert One-on-One J2EE」ではSpringの背景で考えられているアーキテクチャの基礎的な部分についても触れている。しかしながらアーキテクチャに関するコンセプトは2000年初頭までさかのぼり、うまくいった一連の商用プロジェクト用に私がインフラを開発した経験が反映されている。

2003年1月からSpringはSourceForgeでホスティングされている。今は、とてもアクティブな6人を含めた10人の開発者がいる。

Springのアーキテクチャ的な利点

仕様の話に入る前に、Springがプロジェクトにもたらしてくれる利点について見てみよう。

Springによって、本当にあなたの抱えている問題に対する最もシンプルで実現可能なソリューションを実装することができるようになるのである。そしてこれはとても価値あることだ。

Springは何をしてくれるの?

Springでは多くの機能が提供されているので、個々の主な機能についてざっと概観しよう。

ミッションステートメント

まずは、Springのスコープを明らかにしよう。Springは多くの分野をカバーするが、何に注力し、何に注力してはいけないかという明確なビジョンが我々にはあるのだ。

Springのメインとなる目標はJ2EEをより使いやすく、また優れたプログラミングの実施を促進することである。

Springは車輪の再発明はやらない。よって、ロギングパッケージやコネクションプール、分散トランザクション調停がSpringにないことに気づくだろう。これらは全てオープンソースプロジェクト(Commons Loggingのように。これは、我々が全てのログ出力に使っているものだ。あるいはCommons DBCP)やあるいはあなたのアプリケーションサーバで提供されているものだ。同じ理由で、我々はO/Rマッピングレイヤについても提供しない。この問題に対しては、HibernateやJDOのような優れたソリューションがあるからだ。

Springでは既存のテクノロジをより使いやすくすることを目標としている。例えば、低レベルトランザクション調停は我々のターゲットではないがJTA上の抽象レイヤやその他のトランザクションストラテジを提供している。

もし我々が何か新しいものを提供できると思わなければ、Springは他のオープンソースプロジェクトとは直接には競合しない。例えば、多くの開発者のように、我々にもStrutsに決して満足できず、MVC Webフレームワークには改善の余地があると考えている。いくつかの点において、軽量IoCコンテナやAOPフレームワークのように、Springには直接競合する部分があるが、これらは解決ソリューションがポピュラーになっていない分野だ。(Springはこの分野のパイオニアだったのだ)。

Springではさらに内部一貫性による利点がある。開発者はみな、同じ賛美歌シート、つまり「Expert One-on-One J2EE Design and Development」に忠実なままの基本的な考え方で歌を奏でている。そして我々もいろんな分野に跨るInversion of Controlのような、その中心的なコンセプトを使うことができた。

Springはどんなアプリケーションサーバでも同じように使うことができる。もちろん、このポータビリティを保証することは常にチャレンジであるが、我々は何かのプラットフォームに特化したり、スタンダードから外れることは避け、WebLogicやTomcat、Resin、JBoss、WebShereやその他のアプリケーションサーバのユーザをサポートする。

Inversion of controlコンテナ

Springの設計上核となっているものはorg.springframework.beansパッケージであり、これはJavaBeans動作するように設計されたものである。このパッケージは通常、ユーザが直接利用することはないが、他の多くの機能の土台として役に立っている。

その次に抽象度の高いレイヤは「ビーンファクトリ」だ。Springビーンファクトリは汎用のファクトリで、オブジェクトを名前で検索できるようにし、オブジェクト間の関連を管理できるというものだ。

ビーンファクトリでは2つのオブジェクトのモードをサポートしている。

org.springframework.beans.factoryはシンプルなインタフェースなので、一連の基礎となる記憶方法としてインプリメントすることが可能だ。これが必要なユーザはあまりいないがあなたのものも容易にインプリメントできる。最も一般的に利用されるビーンファクトリの定義は次のとおり。

ビーン定義は各々、POJO(クラス名とJavaBean初期化プロパティが定義されている)やファクトリビーンにすることができる。ファクトリビーンインタフェースによって1枚皮をかぶせる。通常これは、AOPやその他のアプローチで用いられるプロキシオブジェクト、例えば宣言的なトランザクション管理を追加するプロキシを生成するのに用いられる。(これは、概念的にはEJBインターセプションに似ているが、実際はもっと単純に動くものだ。)

BeanFaftoryは先祖から定義を「継承している」階層に自由に参加させることができる。このことで独自のオブジェクト群を抱えるコントローラサーブレットのような個々のリソース間でアプリケーション全体で共通のコンフィグレーションを共有することが可能となる。

JavaBeansを使おうとする動機は、「Expert One-on-One J2EE Design and Development」の4章に書いた。これは、ServerSideでフリーのPDFが閲覧可能だ。(http://www.theserverside.com/articles/article.jsp?l=RodJohnsonInterview)

BeanFactoryの概念によってSpringはInversion of Controlコンテナである。(私はこのコンテナという用語はどうもしっくりこない。EJBコンテナのようなヘビーウェイトなコンテナのイメージが沸いてしまうからだ。)SpringのBeanFactoryはコード一行で生成することができるコンテナで、特別なデプロイ手順を必要とはしないのだ。)

Inversion of Controlの背景にある考え方は、「電話してこないで!必要だったらこちらから電話するから」というハリウッドの原理で表現されることが多い。IoCでは何かをするときに発生する責任をアプリケーションコードからフレームワークへ移す。これは、コンフィグレーションがこれに関係するということは、EJBのような伝統的なコンテナアーキテクチャでは、コンポネントはコンテナを「オブジェクトXはどこで、これは私の作業に必要なものだ」と言って呼ぶところ、IoCではコンテナがそのコンポネントはオブジェクトXが必要だと指摘し、実行時にそれを提供する。 このコンテナではこの指摘は(JavaBeanプロパティのような)メソッドのシグネチャを、そしてXMLのようなコンフィグレーションデータ頼りに、行っている。

IoCには様々な重要な利点がある。例えばこのようなものだ。

この最後のポイントは強調しておくべきだろう。IoCはアプリケーションコードのコンテナへの依存性を最小限にするという点でEJBのようなこれまでのコンテナアーキテクチャとは異なるのだ。これはつまり、あなたのビジネスオブジェクトが潜在的に別のIoCフレームワーク、あるいは任意のフレームワークの外側でコードを変更することなく実行することができるということなのだ。

私やSpringユーザの経験では、IoCがアプリケーションコードにもたらす利点は強調しづらいものだ。

J2EEコミュニティではようやくにぎわってきたが、IoCという概念はそれほど新しいものではない。IoCコンテナは他にもある。著名なものとしては、ApacheのAvalonやPicoContainer、HiveMindがある。Avalonは強力だし長い歴史があるにも係わらず有名にはなっていない。Avalonはかなりヘビーウェイトかつ複雑で他の新しいIoCソリューションに比べてさしでがましいようだ。PicoContainerはライトウェイトでJavaBeanプロパティではなく、コンストラクタで依存性を表現する点を強調している。Springとは違い、各型のたった1つのオブジェクトが定義できるように設計されている(おそらくJavaコードではない任意のメタデータを排除した結果による制限だろう)。SpringとPicoContainerや他のIoCフレームワークを比較については、Springのウェブサイト(http://www.springframework.org/docs/lightweight_container.html)にある「The Spring Framework - A Lightweight Container」という記事を参照して欲しい。このページにはPicoContainerのウェブサイトへのリンクも用意してある。

Springのビーンファクトリは非常に軽量だ。ユーザはアプレット内部でもスタンドアロンのSwingアプリケーションででもそれを使うことができる。(EJBコンテナといっしょでもちゃんと動く)。特別なデプロイ手順はなく、立ち上げに時間を要することもない。アプリケーションの任意のレイヤで即座にコンテナをインスタンス化することができるのでとても便利なのだ。

SpringのビーンファクトリのコンセプトはSpring全体で用いられており、これは、Springが内部的に一貫性を持っているということの一番の根拠になっている。Springは他にIoCコンテナに関してもユニークで、フレームワークの全ての機能を通して基本コンセプトとしてIoCを用いている。

アプリケーション開発者にとって最も重要なことは、一つあるいはそれ以上のビーンファクトリがビジネスオブジェクトのために定義されたレイヤを提供する、ということだ。これはローカルセッションビーンに似てはいるが、これと比較してもいたってシンプルなものだ。EJBとは違い、このレイヤにあるオブジェクトには相互関係を持たせることができ、その関係は所持しているファクトリで管理される。ビジネスオブジェクトのレイヤが既に定義されていることは、有益なアーキテクチャにとってとても重要なことだ。

SpringのApplicationContextはビーンファクトリのサブインタフェースであり、下記をサポートする。

XmlBeanFactoryの例

Springユーザは通常アプリケーションの設定を"bean definition"ファイルにXMLで記述する。Spring XMLビーン定義文書のルートは<beans>要素である。この<beans>要素には1つ以上 <bean>定義がある。通常は、各々のビーン定義でクラスやプロパティを指定するが、この他にもコードからこのビーンにアクセスするのに名前として使うIDを指定する必要がある。

ここで、簡単な例を見てみよう。この例は、J2EEアプリケーションでよく見られるような関連をもつ3つのアプリケーションオブジェクトを設定するものだ。

下記に挙げる例では、Jakarta Commons DBCPプロジェクトのBasicDataSourceを使う。このクラスは、(他の多く既存のクラスと同様に)Java-Beanスタイルの設定を使うので、Springビーンファクトリで簡単に使うことができる。シャットダウンの際に呼ばないといけないcloseメソッドは、Springの"destroy-method"属性から登録することができ、BasicDataSourceにSpringインタフェースを実装しないといけないのを避けることができる。

  <beans>
    <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
      <property name="driverClassName">
        <value>com.mysql.jdbc.Driver</value>
      </property>
      <property name="url">
        <value>jdbc:mysql://localhost:3306/mydb</value>
      </property>
      <property name="username">
        <value>root</value>
      </property>
    </bean>
  </beans>

我々が関心のあるBasicDataSourceのプロパティは全部文字列なので、値は、<value>要素で指定する。Springでは標準のJavaBeanプロパティエディタのメカニズムを使っており、必要であれば文字列表現を他の型に変換することができる。

それでは、このDataSourceへのビーン参照を持つDAOを定義しよう。ビーン間の関連は、<ref>要素を使って指定する:

  <bean id="exampleDataAccessObject"
		class="example.ExampleDataAccessObject">
    <property name="dataSource"><ref bean="myDataSource"/></property>
  </bean>

このビジネスオブジェクトはDAOへのリファレンスと、int型のプロパティ(exampleParam)を持っている。

<bean id="exampleBusinessObject"
		class="example.ExampleBusinessObject">
    <property name="dataAccessObject"><ref bean="exampleDataAccessObject"/></property>
    <property name="exampleParam"><value>10</value></property>
  </bean>

</beans>

オブジェクト間の関連は、この例のように通常コンフィグレーションで明示的に設定する。我々はこれはいいことだと思う。でも、Springでは、PicoContainerにならって我々が"autowire"と呼んでいる、ビーン間の依存性がわかるものも提供している。これを用意したことによる制限は、PicoContainerと同様に、もし、特定の型のビーンが複数あると、どのインスタンスがその型の依存性を解決すべきなのかを算出することが不可能だということだ。肯定的には、ファクトリが初期化されたときには得られる依存性が不十分なのだ。(Springでは、さらに明示的なコンフィグレーションのための依存性チェックが可能であり、このゴールを達成することができる)。

もし、この関連性を明示的にコーディングしたいのであれば、上述の例では、下記のようにしてautowire機能を使うことができた。

<bean id="exampleBusinessObject"
	class="example.ExampleBusinessObject"
	autowire="byType">

    <property name="exampleParam"><value>10</value></property>
 </bean>

この使い方で、SpringはexampleBusinessObjectdataSourceプロパティに現在のビーンファクトリで見つかるdataSourceの実装が設定されているはずだ。もし存在しないか、ビーンファクトリが要求している型のビーンが複数あればエラーになる。これは、リファレンスではないので、まだexampleParamプロパティを設定する必要がある。

Autowireのサポートや依存性チェックは既にCVSに登録されていて、Spring 1.0M2(2003/10/20予定)で利用可能になる。本稿で議論しているその他の全ての機能については今のの1.0M1リリースで利用可能だ。

Javaコードを変更することなくXMLファイルが変更できるように、Javaコードから関連を外在化することはそれをコード化すること以上に大きなメリットがある。例えば、我々はmyDataSourceビーンの定義を他のコネクションプールやテストデータソースを使うために別のビーンクラスを参照するように変更するのは簡単にできた。データソースを他のXML アプリケーションサーバSpringのJNDIロケーションファクトリビーンを使ってデータソースを単一の他のXMLベースのアプリケーションサーバから取得することができた。

ここで、ビジネスオブジェクトに関するJavaコードの例をみてみよう。下記のコードでは、Springへ依存していないということに注目して欲しい。EJBコンテナとは違って、Springビーンファクトリは侵略的ではないのだ。通常は、アプリケーションオブジェクトでこういったことを意識してコーディングする必要はないのだ。

 
public class ExampleBusinessObject implements MyBusinessObject {

	private ExampleDataAccessObject dao;
	private int exampleParam;

	public void setDataAccessObject(ExampleDataAccessObject dao) {
		this.dao = dao;
	}

	public void setExampleParam(int exampleParam) {
		this.exampleParam = exampleParam;
	}

	public void myBusinessMethod() {
		// do stuff using dao
	}
}

参照プロパティ設定子に注目して欲しい。これは、ビーン定義文書では、XML参照に相当するものだ。これは、オブジェクトを使うまえに、Springによって起動されるものである。

このようなアプリケーションビーンではSpringに依存する必要はない。ビーンでは、Springインタフェースや拡張Springクラスを実装する必要もない。単に、JavaBeans命名規約に注意する必要があるだけだ。Springアプリケーションコンテキスト外、例えばテスト環境でこれを再利用するのは簡単だ。デフォルトコンストラクタでインスタンス化して、プロパティを一つ一つsetDataSource()setExampleParam()メソッドで設定する。引数のないコンストラクタさえあれば、1行でプログラマティックコンストラクションをサポートしたいのであれば、自由に複数のプロパティを要する他のコンストラクタを定義することができる。

ビジネスオブジェクトインタフェースの呼び出し側に宣言されていないJavaBeanプロパティが動くということに注目して欲しい。これは、実装についての詳細なのだ。我々は、異なるビーンプロパティをもつ異なる実装のクラスを接続されるオブジェクトや呼び出しコードに手を加えずに"プラグイン"することができたのだ。

もちろん、Spring XMLビーンファクトリにはここで述べられていることよりももっと可能性があるが、本稿ではあなた方へ基本アプローチを提示しなければならない。簡単なプロパティや、JavaBeansプロパティエディタが用意されているプロパティと同様、Springでは自動的にリストやマップ、Java.util.Propertiesをハンドリングすることができる。

ビーンファクトリやアプリケーションコンテキストは通常下記のような、J2EEサーバで定義されているスコープとの関連がある。

J2EE仕様で提供されているこれらのフックは一般に、ビーンファクトリのブートストラップでシングルトンを使う必要性を避けている。

しかしながら、ビーンファクトリをプログラムでインスタンス化することは望むのであればたいしたことではない。例えば、我々はビーンファクトリを生成し、上記で定義したビジネスオブジェクトへのリファレンスを下記の3行のコードで取得することができた。

  InputStream is = getClass().getResourceAsStream("myFile.xml");
  XmlBeanFactory bf = new XmlBeanFactory(is);
  MyBusinessObject mbo = (MyBusinessObject) bf.getBean("exampleBusinessObject");

このコードはアプリケーションサーバの外で動くものだ。Spring IoCコンテナはピュアJavaなのでJ2EEにすら依存していない。

JDBCの抽象化とデータアクセス例外の階層

データアクセスはSpringが光り輝く別の分野だ。

JDBCは基盤になっているデータベースを本当にうまく抽象化しているが、APIがとても使い辛い。難点としては以下のようなものがある。

Springではこの問題に対して2つの方法で対処する。

Springでは、2つのレベルでJDBC APIを提供している。1つは、org.springframework.jdbc.coreパッケージで、コールバックを使って制御 - つまり、エラーハンドリングやコネクションの取得、解放- をアプリケーションコードからフレームワーク内部へ移すためのものだ。これは異なるInversion of Controlのタイプだが、コンフィグレーション管理に使っているものと同じぐらい有益なものだ。

Springでは似たようなコールバックアプローチを、JDO(PersistenceManagerの取得や放棄)、(JTAを用いた)トランザクション管理やJNDIのようなリソースを取得したり解放するための特別な手順を含む様々な他のAPIを用意するために使う。このようなコールバックを実行するSpringクラスはテンプレートと呼ばれる。

例えば、SpringのJdbcTemplateオブジェクトは下記のリストのようにSQL問い合わせや結果を退避するのに使われる。

  JdbcTemplate template = new JdbcTemplate(dataSource);
  final List names = new LinkedList();
  template.query("SELECT USER.NAME FROM USER",new RowCallbackHandler() {
    public void processRow(ResultSet rs) throws SQLException {
      names.add(rs.getString(1));
    }
  });

コールバックされるアプリケーションコードでは自由にSQLExceptionを投げられるという点に注目して欲しい。Springではどんな例外でもキャッチできるし、またそれ自身の階層で再度スローすることができる。アプリケーション開発者はどの例外をキャッチしたりハンドリングするかを選択することができる。

JdbcTemplateではあらかじめ用意されたステートメントやバッチによる更新などの異なるシナリオをサポートするために多くのメソッドが提供される。SpringにおけるJDBCの抽象化は、たとえ巨大な結果セットを扱うアプリケーションのケースでさえも標準のJDBCよりも性能のオーバヘッドが低く抑えられている。

これよりも高いレベルのJDBC抽象化はorg.springframework.jdbc.objectパッケージにある。これは、コアJDBCコールバック機能で構築されているが、RDBMS操作 - 問い合わせでも更新、ストアドプロシージャでも- がJavaオブジェクトとしてモデリングされているAPIを提供する。このAPIは部分的に直感的でとても使いやすいと私が感じたJDO問い合わせAPIにインスパイアされたものだ。

ユーザオブジェクトを返すための問い合わせオブジェクトはこのような感じだ。

class UserQuery extends MappingSqlQuery {
  public UserQuery(DataSource datasource) {
    super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?");
    declareParameter(new SqlParameter(Types.NUMERIC));
    compile();
  }

  // Map a result set row to a Java object
  protected Object mapRow(ResultSet rs, int rownum) throws SQLException {
    User user = new User();
    user.setId(rs.getLong("USER_ID"));
    user.setForename(rs.getString("FORENAME"));
    return user;
  }

  public User findUser(long id) {
    // Use superclass convenience method to provide strong typing
    return (User) findObject(id);
  }
}

このクラスは以下のように使うことが可能。

  User user = userQuery.findUser(25);

このオブジェクトはDAOでは多くの場合インナークラスだ。これはサブクラスで変なことをしなければスレッドセーフだ。

他に、org.springframework.jdbc.objectパッケージで重要なクラスとしてStoredProcedureクラスがある。Springでは、ビジネスメソッドを1つもったJavaクラスで代用してストアドプロシージャを可能にしている。好みによって、そのストアドプロシージャを実装したインタフェースを定義することができる。これは、アプリケーションコードをストアドプロシージャを使うということに依存しないようにすることができる、ということなのだ。

Springにおけるデータアクセス例外階層は未チェックの(ランタイム)例外が基になっている。いくつかのプロジェクトでSpringを使ってみて、これが正しい判断だったとますます確信しているのだ。

データアクセス例外は通常復元できない。例えば、データベースに接続できないと、特定のビジネスオブジェクトは恐らくその問題に対処できないだろう。潜在的な例外の1つは楽観的ロックを妨害することだが、全てのアプリケーションが楽観的ロックを使うとは限らない。通常正しく処理ができない致命的な例外をキャッチするように無理やりコードを書くことはいけないことだ。サーブレットやEJBコンテナのようにトップレベルのハンドラに伝播させるのが通常は適切だろう。Springのデータアクセス例外は全てDataAccessExceptionのサブクラスなので、全てのSpringのデータアクセス例外をキャッチすると決めてしまえば実際にそうすることは簡単だ。

もし未チェックのデータアクセス例外から復旧したいのであれば、そうすることも可能だ、ということに注目していただきたい。復旧可能な条件だけを扱うようにコードを書くことが可能なのだ。例えば、楽観的ロック妨害しか復旧できないと考えているのであれば、下記のようにSpring DAOを使ってコードを書くことができる。

  try {
    // do work
  }
  catch (OptimisticLockingFailureException ex) {
    // I'm interested in this
  }

もし、Springのデータアクセス例外がチェックされたものであれば、下記のようなコードを書く必要があるだろう。とにかく、これを書くと決めることができたと言う点に注目して欲しい。

  try {
    // do work
  }
  catch (OptimisticLockingFailureException ex) {
    // I'm interested in this
  }
  catch (DataAccessException ex) {
    // Fatal; just rethrow it
  }

最初の例に対する潜在的な反論 - コンパイラは無理やり潜在的に復旧可能な例外をハンドリングすることはできない - は2つ目にも当てはまる。従って無理やりベース例外(DataAccessException)をキャッチしようとするとコンパイラがサブクラス(OptimisticLockingFailureException)をチェックすることができなくなってしまう。よって、コンパイラは復旧不可能な問題を処理するためのコードを書かせるが、復旧可能な問題を扱わせるのに何の手助けもしないだろう。

Springで未チェックのデータアクセス例外を使うことは、多くの(おそらく大部分の)成功した永続フレームワークと整合が取れている。(もちろん、これは、部分的にJDOにインスパイアされている)。JDBCはチェック済み例外を扱うわずかなデータアクセスAPIのうちの1つだ。例えばTopLinkやJDOでは未チェック例外を排他的に使う。Gavin Kingは今ではHibernateが未チェック例外を選ぶべきだったと信じているのだ。

SpringのJDBCではいくつかのやり方が利用可能だ。

実際我々は、この全てが結局は本質的な生産性の向上とバグを減らすことになることがわかった。私はかつてJDBCコードを書くのが嫌だった。今では不意のJDBCリソース管理よりも実行したいSQLに集中できることがわかったのだ。

SpringのJDBC抽象化は、望めばSpringの他のパーツを使っていることに注力することなくスタンドアロンとして使うことができる。

O/Rマッピングの統合

リレーショナルデータアクセスを使うのではなく、O/Rマッピングを使いたいことももちろんあるだろう。全面的なアプリケーションフレームワークではこれもサポートしなければならない。従ってSpringではHibernate 2.xとJDOを外部で統合している。このデータアクセスアーキテクチャによって任意の基幹データアクセステクノロジとの統合が可能となっている。SpringやHibernateは特に上手く統合されている。

なぜ、Hibernateを直接使うのではなく、HibernateプラスSpringを使うのだろうか。それは、下記に挙げた観点でSpringが十分に価値があるからだ。

トランザクション管理

データアクセスAPIの抽象化は十分ではない。トランザクション管理についてもっと考慮する必要があるのだ。JTAは明確な解決策ではあるが、直接使用するにはやっかいなAPIだし、そのため、多くのJ2EE開発者は、EJB CMTがトランザクション管理に関する唯一の合理的な代替案だと考えている。

Springではトランザクション管理のために独自の抽象化を提供している。Springではこれを配布のために利用している。

Springにおけるトランザクションの抽象化は、JTAやその他のトランザクション管理テクノロジと結合していない、と言う点で独特なものだ。Springではアプリケーションコードから(JDBCのような)土台となるトランザクションインフラを切り離すというトランザクション戦略のコンセプトを用いているのだ。

なぜ、こういったことに注意を払わなければいけないのだろうか。JTAはトランザクション管理の最適解ではないのだろうか。もし、データベースを1つしか使わないアプリケーションを書いていたら、JTAの複雑さは必要ないだろうし、XAトランザクションや2フェーズコミットには関心を払わないだろう。そのような機能を提供するハイエンドなアプリケーションサーバすら必要じゃないかもしれない。しかし、一方で、コードを複数のデータソースが扱えるように書き直さないといけないようになるのは望んでいないだろう。

JDBCやHibernateトランザクションを直接使ってJTAのオーバヘッドを避けることを決めたばあいを想像して欲しい。複数のデータソースを使う必要があるのであれば、トランザクション管理のコードを全て剥ぎ取って、JTAトランザクション処理に置換えないといけない。これはあまり楽しいものではないが、これによって私自身も含めてほとんどのJ2EEのライターがグローバルJTAトランザクションを排他的に使うことを推奨するようになった。しかしながら、Springのトランザクション抽象化機能を使えば、トランザクション戦略が実際に使っていたのがJDBCであろうとHibernateであろうとSpringにJTAを使うように再設定するだけだ。これはコードの修正ではなく、設定の修正だ。よって、Springでは、スケールアップと同様スケールダウンも可能なアプリケーションを書くことができるようになるのだ。

AOP

最近、企業関連のAOPソリューションの適用について興味深いことがたくさんあった。

SpringにおけるAOPサポートの最初の目標は、J2EEサービスをPOJOに提供することだ。これは、JBoss4の目的と似ている。しかしながら、SpringのAOPはアプリケーションサーバ間のポータビリティについてアドバンテージがあり、そのためベンダに縛られるリスクがない。これは、WebコンテナでもEJBコンテナでも動作し、WebLogic,Tomcat,JBoss,Resin,Orionやその他のアプリケーションサーバやWebコンテナで実績がある。

SpringのAOPはメソッドインターセプトをサポートする。キーのAOPコンセプトがサポートするものは以下のものが含まれる。

Springではステートフルインタセプタ(アドバイスされたオブジェクトごとに1つのインスタンス)とステートレスインタセプタ(全てのアドバイスに対して1つのインスタンス)の両方をサポートする。

Springではフィールドインターセプトはサポートしない。これは、設計上熟慮した上で決定したことだ。私は常々フィールドインターセプトはカプセル化を壊すものだと考えている。私はAOPを、OOPに反するものではなく、OOPを引き立たせるものだと思いたい。もし、5年や10年もの間、AOPの学習曲線上をなぞり、AOPがアプリケーション設計のトップの座席を与える快適さを感じたとしても私は驚かないだろう。(しかしながら、この点で、AspectJのように言語に基づいた解決策は今日よりももっと魅力的かもしれない。)

SpringではAOPを(インタフェースが存在する)動的プロキシか、(クラスのプロキシを可能にする)CGLIBの実行時バイトコード生成を使って実装してある。このアプローチは双方ともどんなアプリケーションサーバででも動作する。

SpringはAOPアライアンスインタフェース(http://www.sourceforge.net/projects/aopalliance/)を実装した最初のAOPフレームワークだ。これは、 AOPフレームワーク間でインターセプタを透過にするインタフェースを定義しようという試みを表している。

これは、現在進行中であり、TheServerSideやその他のところで このようなインターセプタが「本当のAOP」であるかどうかについてまだ議論に結論は出てはいないし、私はこれがなんと呼ばれるかについては特に注意を払っていない。単に、実際に役に立てるのかどうかだ。私は、等しく「宣言的ミドルウェア」と喜んで呼ぼうと思う。SpringのAOPは、単純で軽い、ステートレスセッションビーンの代替案で、モノシリックなEJBコンテナの必要性をなくし、必要とするサービスだけを持った独自のコンテナを作れるようにしたものだと考えて欲しい。ローカルなステートレスセッションビーンによって推奨された粒度が明らかにされるに似ているので我々はどんなアドバイスでもどんなPOJOでもお勧めはしない。(しかしながら、EJBとは違って、適切な場面でまれなシナリオできめ細かいオブジェクトにSpringのAOPを自由に適用することが可能だ。)

Springにおけるアドバイスオブジェクトは、クラスローダレベルではなく、インスタンスレベルなので、同じクラスに複数のインスタンスに異なるアドバイスをつけたり、アドバイスの無いインスタンスをアドバイスつきインスタンスと同じように使うことが可能なのだ。

恐らくSpring AOPの最も一般的な利用は、宣言的なトランザクション管理のためのものだ。これは、上述したTransactionTemplate抽象をベースに構築されていて、宣言的トランザクション管理をいろんなPOJOに伝えることができる。トランザクション戦略によって、ベースとなるメカニズムはJTAやJDBC,Hibernateやその他のトランザクション管理に関するAPIが利用できる。

Springの宣言的トランザクション管理はEJB CMTと似ているが、下記の点で異なる。

もちろん、SpringAOPをアプリケーションに特化したアスペクトを実装するのに利用するのも可能だ。これを利用するのもしないのも AOPの概念を使うことがSpringの能力よりもどれだけ快適になるかの程度によるが、とても役に立つだろう。我々がこれまで見たことのある成功事例には以下のようなものがある。

アプリケーションに特有のアスペクトは、多くのメソッドで何度も出てくる決り文句のようなコードをなくすのに威力を発揮する手だ。

Spring AOPはSpringのビーンファクトリの概念を透過的に統合されている。Springビーンファクトリからオブジェクトを取得するコードはアドバイスされているかどうかを知っている必要がない。どのようなオブジェクトであっても、その契約はそのオブジェクトが実装するインタフェースによって定義される

下記のXMLの一節は、AOPプロキシの定義の仕方を示したものだ。

  <bean id="myTest" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces">
      <value>org.springframework.beans.ITestBean</value>
    </property>
    <property name="interceptorNames">
      <list>
        <value>txInterceptor</value>
        <value>target</value>
      </list>
    </property>
  </bean>

このクラスのビーン定義は通常AOPフレームワークのProxyFactoryBeanである。リファレンスに使われたり、BeanFactorygetBean()メソッドから返されるビーンの型はプロキシインタフェースに依存するのだが。(多重プロキシメソッドはサポートされている)。ProxyFactoryBeanの「interceptorNames」プロパティはStringのリストを取る。(ビーン名はビーンリファレンスよりも頻繁に使われるはずだ、プロキシが「prototype」の場合シングルトンビーン定義よりもステートフルインタセプタの新しいインスタンスが生成される必要があるかもしれないので)。このリストにある名前はインタセプタかあるいはポイントカット(インタセプタといつそれらが適用されるのかに関する情報)だろう。この前述のリストにある「target」の値は自動的にターゲットオブジェクトをラッピングする「invoker interceptor」を生成する。これはプロキシインタフェースを実装するファクトリ中のビーンの名前だ。この例でのmyTestビーンはビーンファクトリ中の他のビーンのように使われる。例えば、他のオブジェクトが<ref>要素から参照することができ、この参照はSpring IoCによって設定される。

また、これはめったに使われることはないが、BeanFactoryを使わずにAOPプロキシをプログラマティックに生成することも可能だ。

  TestBean target = new TestBean();
  DebugInterceptor di = new DebugInterceptor();
  MyInterceptor mi = new MyInterceptor();
  ProxyFactory factory = new ProxyFactory(target);
  factory.addInterceptor(0, di);
  factory.addInterceptor(1, mi);
  // An "invoker interceptor" is automatically added to wrap the target
  ITestBean tb = (ITestBean) factory.getProxy();

我々は、Javaコードとアプリケーションの結合性を弱くすることは一般的にベストである信じているし、これは、AOPについても例外ではない。

SpringのこのAOPの実現という点における一番の競合はJon TirsenのNanning Aspects(http://nanning.codehaus.org/)だ。

私は、AOPをエンタープライズサービスを配備するためにEJBの代替案として使うことの重要性が増していると思う。これは、前進しているSpringでさらに重要視していくつもりだ。

MVCウェブフレームワーク

Springにはパワフルで柔軟性の高いMVC Webフレームワークが用意されている。

SpringのMVCモデルは、StrutsのMVCモデルに一番似ている。Spring Controllerは全てのクライアントの振る舞いが単一のインスタンス上でが実行されるマルチスレッドサービスオブジェクトであるという点でStruts Action に似ている。しかしながら、我々はSpringのMVCがStrutsに比べてとても大きな利点を持っていると考えている。例えば以下の点についてだ。

Struts1.1でのように、Spring MVCアプリケーションで必要になるのと同じだけのディスパッチャサーブレットを用意することができる。

下記の例は、 簡単なSpring Controllerが同じアプリケーションコンテキストで定義されたビジネスオブジェクトにどのようにアクセスできるかを示したものだ。このコントローラはhandleRequest()メソッドでGoogle検索を実行する。

  public class GoogleSearchController implements Controller {
    private IGoogleSearchPort google;
    private String googleKey;
    public void setGoogle(IGoogleSearchPort google) {
      this.google = google;
    }

    public void setGoogleKey(String googleKey) {
      this.googleKey = googleKey;
    }

    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
      String query = request.getParameter("query");
      GoogleSearchResult result =
      // Googleのちゃんとした定義は省略する

      // googleビジネスオブジェクトを使う
      google.doGoogleSearch(this.googleKey, query,
			    start, maxResults, filter, restrict,
			    safeSearch, lr, ie, oe);

      return new ModelAndView("googleResults", "result", result);
    }
  }

このコードが得られるプロトタイプでは、IGoogleSearchPortはGLUEウェブサービスプロキシで、それはSpring FactoryBeanから返されたものだ。しかしながら、このコントローラはこのコントローラが依存しているウェブサービスライブラリからはIoCによって切り離されている。このインタフェースは下記の議論のように純粋なJavaオブジェクト、テストスタブ、擬似オブジェクトあるいはEJBプロキシで実装されたのと同じようにすることができる。このコントローラにはリソースの参照は含まれておらず、ウェブインタラクションをサポートするのに必要なコード以外は含まれていない。

Springではデータバインディング、フォーム、ウィザードやもっと複雑なワークフローもサポートされている。

Thomas RisbergのSpring MVCチュートリアル(http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step.html)がSpring MVCフレームワークの入門にとてもいい。他にも「Web MVC with the Spring Framework(http://www.springframework.org/docs/web_mvc.html)」を参照して欲しい.

もし、あなたが自分の好みのMVCフレームワークに満足しているのであれば、Springがレイヤ構造をもっているので我々のMVCレイヤを除いた残りのSpringを使ってみるのがいいと思う。Springユーザの中には、Springをミドル層の管理やデータアクセスに使ってウェブ層にはStrutsやWebWorkあるいはTapestryを使っている人もいる。

EJBを実装する

もしあなたがEJBを使うのであれば、SpringではEJB実装とEJBアクセスクライアントに関しての重要な利点を提供できる。

ビジネスロジックをEJBファサードの背後にあるPOJOへリファクタリングすることは今ではベストプラクティスとして広く受け入れられている。(とりわけ、EJBがコンテナに大きく依存し、切り離してテストするのが非常に難しいので、これによってビジネスロジックのユニットテストを実施するのが簡単になるからだ。)Springでは、EJB Jarファイルに含まれるXMLドキュメントに従って自動的にBeanFactoryをロードすることでユニットテストをとても簡単にできるようになる、便利なセッションビーンやメッセージドリブンビーンのスーパクラスを提供している。

これはつまり、下記のようにステートレスセッションEJBがこのようにコラボレータを取得して使う、ということだ。

  import org.springframework.ejb.support.AbstractStatelessSessionBean;

  public class MyEJB extends AbstractStatelessSessionBean implements MyBusinessInterface {
    private MyPOJO myPOJO;

    protected void onEjbCreate() {
      this.myPOJO = getBeanFactory().getBean("myPOJO");
    }

    public void myBusinessMethod() {
      this.myPOJO.invokeMethod();
    }
  }

MyPOJOがインタフェースであると仮定して、この実装クラス(および、プリミティブプロパティや他のコラボレータのようなこれが必要とする全てのコンフィグレーション)はXMLのビーンファクトリ定義に隠蔽されている。

Springには下記のように、標準のejb-jar.xmlというデプロイメントデスクリプタにあるejb/BeanFactoryPathという名前の環境変数定義から読み込むXMLドキュメントの場所を知らせる。

  <session>
    <ejb-name>myComponent</ejb-name>
    <local-home>com.test.ejb.myEjbBeanLocalHome</local-home>
    <local>com.mycom.MyComponentLocal</local>
    <ejb-class>com.mycom.MyComponentEJB</ejb-class>
    <session-type>Stateless</session-type>
    <transaction-type>Container</transaction-type>

    <env-entry>
      <env-entry-name>ejb/BeanFactoryPath</env-entry-name>
      <env-entry-type>java.lang.String</env-entry-type>
      <env-entry-value>/myComponent-ejb-beans.xml</env-entry-value></env-entry>
    </env-entry>
  </session>

このmyComponent-ejb-beans.xmlファイルはクラスパス、この場合はEJB Jarファイルのルートからロードされる。各EJBごとに独自のXMLドキュメントを指定することができるので、このメカニズムはEJB Jarファイル毎に何度も使うことができる。

SpringのスーパクラスではsetySessionContext()ejbCreate()のようなEJBライフサイクルメソッドを実装が実装されていて、アプリケーション開発者にはSpringのonEjbCreate()メソッドの実装が自由にできるようにしてある。

EJBを使う

SpringではEJBを実装するのと同じように、とても簡単にEJBを使えるようになっている。多くのEJBアプリケーションではサービスロケータやビジネスデリケートパターンを使う。これはクライアントコード全体にわたってJNDIルックアップをするよりはましだけど、通常の実装としてはとても不利だ。例えば下記のような点について。

こういう様々な理由のため、SunアドベンチャービルダやOTN J2EEバーチャルショッピングモールのようなアプリケーションにあるような従来のEJBアクセスは生産性を下げることになり、とても複雑なものになってしまうのだ。

Springは、これをコードレスビジネスデリゲートで克服する。Springでは、他のサービスロケータや他のJDNIルックアップ、あるいは ハードコーディングされた複製メソッドは実際の値を追加しない限り必要ない。

例えば、ローカルEJBを使うウェブコントローラがあると考えて欲しい。我々はベストプラクティスに従ってEJBビジネスメソッドインタフェースパターンを使うだろう。その結果、EJBのローカルインタフェースはEJBに準拠しないビジネスメソッドインタフェースを拡張する。(これをやってしまう一番の理由は、ローカルインタフェースのメソッドシグネチャと、ビーンの実装クラスの同期が自動的に行われることを保証するためだ)。このビジネスメソッドインタフェースをMyComponentと呼ぼう。もちろん、この他にもローカルホームインタフェースを実装し、セッションビーンを実装したビーン実装クラスとMyComponentビジネスメソッドインタフェースを提供する必要がある。

SpringのEJBアクセスでは、JavaコードでWeb層コントローラをEJB実装を結びつけるためにやらないといけないことは、 MyComponent型のセッターメソッドをコントローラに見せることだけだ。これで下記のようにインスタンス変数としてリファレンスを保存する。

  private MyComponent myComponent;

  public void setMyComponent(MyComponent myComponent) {
    this.myComponent = myComponent;
  }

我々は、引き続きこのインスタンス変数をあらゆるビジネスメソッド中で使用することができる。

Springでは、このようなXMLビーン定義エントリから残りの作業を自動的に行う。LocalStatelessSessionProxyFactoryBeanは汎用的なファクトリビーンで、これはどんなEJBにも使うことができる。このファクトリビーンが生成するオブジェクトはSpringによってMyComponent型へ自動的にキャストすることができる。

  <bean id="myComponent" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
    <property name="jndiName"><value>myComponent</value></property>
    <property name="businessInterface"><value>com.mycom.MyComponent</value></property>
  </bean>

  <bean id="myController" class = "com.mycom.myController">
    <property name="myComponent"><ref bean="myComponent"/></property>
  </bean>

AOPコンセプトを使ったその結果を役に立たせることを強いられることはないが、Spring AOPフレームワークのおかげでちょっとしたハプニングがその背後ではたくさんある。「myComponent」ビーン定義はビジネスメソッドインタフェースを実装したEJBのプロキシを生成する。EJBローカルホームは起動時にキャッシュされ、そのためJDNIルックアップも1回だけだ。EJBが起動されるたびに、このプロキシはローカルEJBのcreate()メソッドを叩いてEJB上の対応するビジネスメソッドを起動する。

myControllerビーン定義はこのプロキシにコントローラクラスのmyControllerプロパティを設定する。

このEJBアクセスメカニズムによってアプリケーションコードは大幅に単純化される。

ベンチマークや実際のアプリケーションでの経験から、このアプローチ(ターゲットEJBのリフレクション的な起動を含めて)のパフォーマンス上オーバヘッドは極小さいものであり、通常の使い方では、検出できないレベルであることが示されている。アプリケーションサーバでは、EJBインフラに関連したコストがあるので我々はEJBへのきめ細かい呼び出しを行いたくない、ということを覚えておいて欲しい。

同様のorg.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBeanファクトリビーンを使って同じアプローチをさらにリモートEJBに適用することも可能だ。しかしながら、リモートEJBのビジネスメソッドインタフェース上のRemoteExceptionを隠すことはできない。

テストする

あなたが引き寄せられたように、私や他のSpring開発者は包括的なユニットテストの重要性についての熱心な信仰者だ。我々はフレームワークが徹底的にユニットテストされ、フレームワーク設計の一番のゴールがフレームワーク上に構築されたアプリケーションを簡単にユニットテストを実施できるようにすることが必要だと信じている。

Springそのものは優れたユニットテストスイートを持っている。我々の1.0M1時点でのユニットテストカバレッジは75%以上で、1.0RC1では80%以上のユニットテストカバレッジを達成したいと考えている。我々はテストファースト開発の利点がこのプロジェクトでとてもリアルなものであることに気づいた。例えば、世界中に散らばったチームとして非常に効率よく動くようになったし、ユーザはCVSスナップショットが安定してきたとか十分に使えるというコメントを寄せている。

我々はSpring上で構築されたアプリケーションのテストがとても簡単なのは以下の理由からだと考えている。

Springビーンファクトリをコンテナの外からセットアップできることで、開発プロセスにとって興味深い副産物が得られる。いくつかのSpringを使ったウェブアプリケーションプロジェクトでは、作業はビジネスインタフェースを定義し ウェブコンテナの外部の実装のテストを統合することから始まった。ビジネス機能が本質的に完成した後はウェブインタフェースを提供するための薄い層を追加するだけだ。

誰がSpringを使っているの?

Springは比較的新しいプロジェクトではあるが、尊敬に値し、なおかつ成長し続けるユーザコミュニティを持っている。またSpringを用いた多くのアプリケーション製品が既に存在する。ユーザには著名な多国籍投資銀行(大規模プロジェクト)、有名なドットコム企業や様々なウェブ開発コンサルタント、ヘルスケア企業や学術機関も含まれている。

ほとんどのユーザはSpringの全パーツを使っているが、一部コンポネントを単独で使用するユーザもいる。例えば多くのユーザは我々のJDBCや別のデータアクセス機能を使うところから始めている。

ロードマップ

我々の一番の優先事項はSpringの1.0を遅くても今年中にリリースすることだ。でも長期的なゴールもいくつかある。

バージョン1.0最終版に向けて予定されている主な拡張は、ソースレベルのメタデータサポートだ。これは、我々のAOPフレームワークを使うためのものだ(でも、制限するものではない)。これによってC#スタイルの属性駆動トランザクション管理が可能になり、宣言的なエンタープライズサービスを典型的な使い方にするのがとても簡単になるだろう。属性のサポートはSpring1.0の最終リリースには取り込まれる予定であり、リリースされる際にはJSR-175と統合されるように設計される予定だ。

1.0以降では、下記のような拡張が予定されている。

アジャイルプロジェクトとして、我々はユーザの要求に第一に従って進めていく。従って、誰も使っていない機能を開発するようなことはしないし、ユーザコミュニティの声に注意深く耳を傾けるつもりだ。

サマリ

SpringはJ2EEの多くの共通の問題を解決する強力なフレームワークだ。

Springではビジネスオブジェクトを管理する一貫した手段を提供し、クラス指向ではなくインタフェース指向のプログラミングのようなベストプラクティスを促進する。Springのアーキテクチャ上の基本はJavaBeanプロパティの利用に基づくInversion of Controlコンテナである。しかしながら、これは全体像のほんの一部に過ぎない。Springでは、アーキテクチャ上の全てのレイヤにおける包括的なソリューションにおいてIoCコンテナを基本的な構造として使っている点で独特なものなのだ。

Springでは独特のデータアクセスの抽象化を提供する。これには、単純で生産性の高いJDBCフレームワークも含まれている。このフレームワークは生産性を大きく改善しエラーの可能性を減少させる。SpringのデータアクセスアーキテクチャはHibernateやその他のO/Rマッピングソリューションとも統合されている。

Springではさらに独特のトランザクション管理の抽象化を提供する。これは、JTAやJDBCのような様々なベースとなるトランザクションテクノロジに対して一貫したプログラミングモデルを可能とする。

Springは標準のJavaで書かれたAOPフレームワークを提供する。これは、宣言的なトランザクション管理やその他のエンタープライズサービスをPOJOに適用したり、あるいは、-もし必要であれば- 自分で独自のアスペクトを実装できるようにするものである。多くのアプリケーションが伝統的にEJBに関連づけられたキーサービスのいくつかを利用していても、EJBの複雑さを回避できるぐらい強力なのだ。

Springでは、強力で柔軟なMVCウェブフレームワークを提供する。これは、IoCコンテナに全面的に統合することが可能だ。

さらなる情報

Springについてもっと知りたければ以下のリソースを参照して欲しい。

我々はSpringのドキュメントやサンプルをよりよいものにすべく最善を尽くしている。またフォームやメーリングリストでの問い合わせに対してのレスポンスの速さについても誇りを持っている。我々はあなたがすぐにコミュニティに参加してくれるの歓迎したい。

著者について

ロッド・ジョンソンはJava開発者およびアーキテクトとして7年以上の経験を積み、J2EEプラットフォームが出現して以来、これに従事している。彼はExpert One-on-One J2EE Dedign and Development(Wrox, 2002)の著者であり、J2EEに関して他の分権にもいくつか寄稿している。現在は、WileyのJ2EEアーキテクチャに関する次の本を執筆中である。Rodは2つのJava仕様策定委員会のメンバであり、常任のカンファレンススピーカである。現在、英国にてコンサルタントとして働いている。