オープンソースCMS「DotNetNuke」のセットアップ
前回はMicrosoft Web Platform Installerを利用して、DotNetNukeとWebMatrixをインストールする方法を紹介した。今回は、DotNetNukeのインストール方法を紹介する。
ブックマークされました!
ブックマークがエラーになりました。もう一度お願いします。

作者 Rod Johnson , 翻訳者 編集部 投稿日 2007年10月20日
Spring 2.0 は 2006 年 10 月に完成し、上述の価値を一層高めました。2005 年12月フロリダで行なわれる Spring エクスペリエンスコンファレンスを前にして、中心チームは一連のさまざまな開発機能を見据えていたので、シンプルさとパワーという2 つの重要なテーマが Spring 2.0 に共通する流れとして際立っており、初期の Spring のテーマと変わっていないことがわかりました。
簡単に決定できた項目もありました。最初から、Spring 2.0 は完全な下位互換性、または可能な限りほぼ完全な下位互換性を持つことは明らかでした。特に、Spring が多くの企業で事実上の標準(デファクトスタンダード)として地位を確立しているとすれば、ユーザー体験の混乱を一切排除することがきわめて重要です。幸いにも、Spring は非侵入的にするための努力を常に払ってきたため、この目標の達成は可能でした。
10 ヶ月間にわたる開発を経て Spring 2.0 への取り組みが進む中、2005 - 2006 年の Spring の使用方法において明らかになったいくつかの動向を考慮する必要性が生じました。
ビジネスアプリケーションの開発者にとって Spring がさらに良い製品になったため、このような要求の厳しいユーザーのニーズに応える努力をする必要がありました。
Spring 2.0 の大きなビジョンとは?
Spring 2.0 によってさまざまな点が増強されるが、その中で最もわかりやすいものはおそらく以下のポイントでしょう。
表面に現れない機能も数多く備わっています。これらはわかりにくいですが、重要な役割を果たしています。
重要な点は、このような機能が、全体的に調和して連携的に機能するように設計されているということです。
この記事は 2 つのパートに分かれています。パート 1(この記事)では、コアコンテナ、XML 構成拡張、AOP の強化、Java 5 に特有の機能について記述します。
パート 2(来月以降に投稿)では、メッセージング、動的な言語のサポート、JPA、Web 層の強化を扱います。また、表面に出てこない数多くの改良点についても取り上げます。
それでは、コード例を示しながら、新機能のいくつかをさらに詳しく見てみましょう。
Spring 2.0 における最もわかりやすい強化点の 1 つは、XML 構成に関するものです。
Spring IoC コンテナは、実際には XML などのメタデータの表現には依存していません。Spring には、Java オブジェクト(BeanDefinition とサブインターフェイス)という形で独自の内部メタデータが組み込まれています。コメントを利用する Java 構成など、XML 構成を補足する代替品に関しては、活発な研究が行なわれています (http://blog.interface21.com/main/2006/11/28/a-java-configuration-option-for-spring/)。
しかし、XML は現在 Spring の構成に使われるケースが最も多く、Spring のコアの構成改善における最大のポイントです。
Spring 2.0 における XML の強化は、シンプルとパワーという 2 つのテーマを的確に表すものです。つまり、XML の強化によって一部の共通タスクの実行が簡素化されるだけでなく、ほかの高度なタスクも可能になっているのです。
これまで、Spring の XML 構成シンタックスと Spring の内部メタデータの間には、1 対 1 の関係がありました。1 つの要素によって、1 つの BeanDefnition が生成されていたのです。
これは私たちが一般的に望むことであり、フレームワークが認識していないアプリケーションのクラスを構成する上で理想的でもあります。
しかし、繰り返し使われることが多い特定のクラス、たとえば、JNDI からオブジェクトを探し出して、挿入可能なオブジェクトとして Spring のコンテナに入れるのに使われる JndiObjectFactory のようなジェネリッククラスのことをフレームワークが知っていなければならないとしたらどうでしょう。複数の Bean 定義を同時に使用しなければ意味がないとしたらどうでしょう。
したがって、新しい形の抽象化によって、重要なメリットがもたらされます。
以下の JNDI のルックアップの例について考えてみましょう。
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jdbc/jpetsore" />
</bean>
これは、サービスロケーターを実装していたひどい時代よりは確かによいですが、完全なものではありません。われわれは常に同じ Bean クラスを使おうとしています。そして(少なくとも、Java 5 を使わないのであれば)、jndiName プロパティが必要ですが、ほかのプロパティは必須でないことを示すメカニズムがありません。
以下のように、Spring 2.0 ではボックスからネーム空間 jee および同じ JNDI ルックアップを実行できるタグが加えられます。
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/jpetstore"/>
これはよりシンプルで明確であり、意図を明確に表現しています。また、より簡潔でもあります。そして、同じスキーマによって、jndiName プロパティが必須であることをとらえ、ツールのサポートを容易にしています。以下の例が示すように、そのほかのオプションのプロパティもこのスキーマで表現されています。
<jee:jndi-lookup id="simple"
jndi-name="jdbc/MyDataSource"
cache="true"
resource-ref="true"
lookup-on-startup="false"
expected-type="com.myapp.DefaultFoo"
proxy-interface="com.myapp.Foo"/>
このような比較的に単純なケースでは、属性名は構成中のクラスのプロパティ名とほとんど同じですが、これは必要でないことに注目してください。属性とプロパティの間にネーミングの対応、つまり 1 対 1 の対応性は必要ありません。また、サブ要素の内容も処理することができます。そして、すでに述べたように、拡張タグから任意の数の Bean 定義を生成することができます。
それでは、カスタムタグの能力を利用して、複数の Bean 定義を生成するようなもっと複雑な例を見てみましょう。
Spring 1.2 以来、@Transactional コメントを Spring に認識させ、プロキシ処理することによって、関係する Bean を自動的に処理可能にすることができるようになりました。その結果、シンプルな展開モデルができました。コメントが付けられた Bean を加えるだけで、自動的に処理可能になります。しかし、それを設定するにはいくらか複雑なマジックが必要でした。必要な共同オブジェクトには、3 つの Bean 定義が必要とされました。3 つの Bean 定義とは、Spring AOP Advisor、TransactionInterceptor、自動プロキシを引き起こすDefaultAdvisorAutoProxyCreator です。このようなマジックの Bean 定義が呪文となり、さまざまなアプリケーションでそのまま利用できましたが、ユーザーが知る必要のない細かい部分が露呈することとなりました。
<bean
class="org.springframework...DefaultAdvisorAutoProxyCreator"/>
<bean class="org.springframework...TransactionAttributeSourceAdvisor">
<property name="transactionInterceptor
ref="transactionInterceptor"/>
</bean>
<bean id="transactionInterceptor"
class="org.springframework...TransactionInterceptor">
<property name="transactionManager"
ref="transactionManager"/>
<property name="transactionAttributeSource">
<bean
class="org.springframework...AnnotationsTransactionAttributeSource">
</bean>
</property>
</bean>
この抽象化のレベルは間違ったものです。ユーザーは、トランザクションを管理する上で、Spring AOP フレームワークの仕組みに関してここまで知る必要はなく、その意図も不明確です。Spring 2.0 では、新しい tx ネーム空間で単一のタグが提供されるため、これらすべての定義が以下のように置き換えられます。
<tx:annotation-driven />
このアウトオブボックスタグによって同じ結果が得られます。このタグは意図を明確に表現しており、トランザクションのコメントを自動的に認識します。それでも、3 つの Bean 定義は、表面に出てこない拡張タグによって生成されますが、それは現在、(正しくは)ユーザーでなくフレームワークが扱う問題です。
Spring 2.0 の拡張タグは、必要に応じてタグ自体の属性やサブ要素の構造を定義できます。ネーム空間を定義する開発担当者は完全に把握しています。Spring 2.0 の拡張タグを処理する NamespaceHandler は、拡張タグから Bean 定義をいくつでも作成できます。
拡張タグを使うためには、DTD ではなく XML スキーマを使い、該当するネーム空間をインポートする必要があります。デフォルトのネーム空間は Bean スキーマである必要があります。以下に示す tx ネーム空間の例では、
<?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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
拡張タグは通常の Bean 定義と組み合わせることができ、任意の数のネーム空間をインポートして同じドキュメントで使用できます。
Spring 2.0 では、複数のアウトオブボックスネームス空間が使用できる。最も重要なポイントを以下に示します。
Spring 2.1 以降では、さらに多くのネーム空間を加えて、このシンプルさを新しい領域でも実現します。コアに最初に追加されるのは、おそらく Spring MVC と JPA の使い方を簡単にするネーム空間です。
拡張メカニズムとして、Spring 2.0 のネーム空間に関連する最も重要な可能性は、Spring のコアの外部にあるでしょう。
ネーム空間を使うと、Spring を基盤とした多くの製品とその構成を簡素化できます。その好例は、Acegi Security for Spring(2007 年の初めに Spring Security というブランドに改称)です。Acegi Security for Spring では、複数の共同 Bean の定義を設定する必要があります。Spring 2.0 のネーム空間はこの定義を格段に簡素化し、もう一度言いますが、意図をより明確に表現します。
Spring と密接に融合している製品は数多くありますが、そのような製品のメリットは明らかです。Tangosol のCoherence の統合はその好例です。
その他の可能性がある例としては、IBM の ObjectGrid など、Spring フレンドリーな構成を採用した製品が挙げられます。現時点では、ObjectGrid は内部で Spring を使っていませんが、JavaBeans で構成することによって、Spring をベースとしたアプリケーションと簡単に融合できるようになっています。拡張スキーマによって、この融合は格段に簡単になります。
XML ドキュメントでは、拡張タグをトップレベル要素として使用できます。したがって、拡張スキーマ要素の前にネーム空間を付ける必要がなくなり、Spring を中心とした構成ではなく、「ネイティブ」な構成に見せることができるようになります(通常、要素はデフォルトのネーム空間にあるため、従来の Spring の Bean 定義には接頭辞を付ける必要がありません)。
時が経つにつれて、JSP 特殊タグと同様、経験から確実な汎用タグを志向するようになるでしょう。ユーザーが、ネーム空間のライブラリを作成してコミュニティにメリットを与えるようになると予想しています。
ネーム空間の実装は比較的簡単であり、以下の3 つの手順で行います。
Spring に付属する spring.handlers ファイルには、「標準」ネーム空間がどのように構成されているかが記述されています。:
http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
さまざまな /META-INF ディレクトリに複数の spring.handlers ファイルを配置することができ、Spring はそれらのファイルを実行時にマージします。
上記の手順を実行すると、新しい拡張を使用することができます。
NameSpaceHandler インターフェイスの実装は難しくありません。W3C DOM 要素を使って、それを処理することで BeanDefinition メタデータを生成します。Spring が XML の構文解析をするので、作成したコードはツリーをたどるだけでよいのです。
public interface NamespaceHandler {
void init();
BeanDefinition parse(Element element, ParserContext parserContext);
BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}
parse() メソッドは最も重要であり、与えられたコンテキストに BeanDefinition を加える役割を果たします。
decorate() メソッドにも注目してください。NamespaceHandler は、スコープ付きのプロキシを作成する以下のシンタックスのように、囲み Bean 定義を装飾することもできます。
<bean id="scopedList" class="java.util.ArrayList" scope="request">
<aop:scoped-proxy/>
</bean>
BeanDefinition メタデータの生成を簡素化するために、Spring 2.0 では便利な新しい BeanDefinitionBuilder クラスが導入され、滑らかなビルダースタイルの API が実現しています。NamespaceHandlers の実装を始めるのに最善の手引きは、Spring コアに同梱されているものです。UtilNamespaceHandler は比較的簡単な例であり、AopNamespaceHandler は、複雑なサブ要素構造の構文解析を行う高度な例です。
かなづちを持っているという理由だけで、すべてのものが釘だとは限りません。これまで見てきたように、Spring 2.0 の拡張メカニズムはさまざまなケースで大きな価値をもたらしますが、十分な理由がないのであれば使うべきではありません。XML 拡張タグは新しい抽象化であるため、新しく覚えなければならないこともあります。Spring の正規の XML フォーマットはすでに数万人の開発担当者になじみの深いものであり、Spring を初めて扱う人にとっても直感的にわかるものです。Spring の XML ファイルは、アプリケーション構造をわかりやすく示すマップまたは設計図です。コンフィギュレーションで不慣れなカスタムタグを多数使っている場合は、必ずしもこうなるとは限りません。
この分野における関連する経験について考えてみましょう。JSP のカスタムタグが好例です。最終的に、JSTL、Struts、Spring MVC タグライブラリといった、経験から生まれたうまく設計されたタグライブラリという形で、このタグは真のメリットを生み出しました。しかし、初期段階では、JSP を混乱させる可能性すらあるため、非常に嫌われていました(1 つか 2 つ自分自身で実装していたため、ここでは経験に基づいて話しています)。
ネーム空間ハンドラは、新しい重要な拡張ポイントであり、そして Spring の価値がある新しい抽象化であると考えてください。これは、Spring の上にサードパーティ製の製品を構築する人たちにとってすばらしいことであり、非常に大規模なプロジェクトに役立ちます。間もなく、これらがない生活を想像するのが難しい時代になります。しかし、エンドユーザーは、これらを消費することよりも実装することに慎重になったほうがよいでしょう。
もちろん、aop、tx、jee など、ボックスから Spring に提供される拡張タグは、近いうちに要素として広く知られているSpring 構成語彙の中心的な部分になるでしょう。もっと冗長な方法で同じことを達成する従来の方法よりも、このようなタグを使うべきであることは確かです。
スキーマに移行することによって、プロパティ値のサブ要素ではなく属性を使って、多少近道をすることができます。このような属性の妥当性は確認されていませんが、私たちは DTD ではなく XML スキーマを使おうとしているので、そのほかのすべての妥当性確認を今でも保持することができます。属性名はプロパティ名なので、いずれにせよ XML 妥当性確認によって付け加えられるものは何もありません。これは、XML 構造ではなく、Java をベースとした妥当性確認の問題です。
単純な 2 つのプロパティを持ち、関連オブジェクトに依存する以下の Java オブジェクトについて考えてみましょう。
public class Person {
private int age;
private String name;
private House house;
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setHouse(House house) {
this.house = house;
}
}
これは、XML を使って以下のように構成できます。
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="com.interface21.spring2.ioc.Person"
p:name="Tony"
p:age="53"
p:house-ref="number10"
/>
<bean class="com.interface21.spring2.ioc.House"
id="number10"
p:name="10 Downing Street"
/>
</beans>
要素ではなく属性を使って、どのようにプロパティが提供されるのかに注目してください。これは、特殊なネーム空間 p のマジックによって機能します。このネーム空間の妥当性は確認されていませんが、Java のプロパティ名と名前が一致する属性を使用することができます。
単純なタイプで、p:name のように、名前空間 p のプロパティ名を使うだけです。そのほかの Spring Bean への参照を挿入する場合は、p:house-ref のように、接尾辞 ?ref を使います。
このショートカットシンタックスは、オートワイヤリングを使いたいときに、特に強制的に使う必要があります。たとえば、以下のような可変部について考えてみましょう。
<bean class="com.interface21.spring2.ioc.Person"
autowire="byType"
p:name="Tony"
p:age="53"
/>
ここでは、house プロパティを設定していません。オートワイヤリングが設定できるからです。また、
Spring 1.0 または 1.1 の使い方の以下の断片を見ると、Spring の構成によって、最近の 2 つの主要なリリース(1.2 と 2.0)における山括弧(<、>)の最少数が大幅に少なくなったことがわかります。
<bean class="com.interface21.spring2.ioc.Person">
<property name="name"><value>"Tony"</value></property>
<property name="age"><value>"53"</value></property>
<property name="house"><ref local="number10" /></property>
</bean>
Spring 1.2 では、ほとんどのケースでサブ要素を要求する代わりに、value 属性と ref 属性を導入しましたが、Spring 2.0 では、pure 属性と simple 属性が使えます。
もちろん、従来の XML フォームは今後も機能する。プロパティ値が複雑で、属性値として非正規または判読不可能な場合に従来の XML フォームを使います。もちろん、既存の構成ファイルを書き換える必要ありません。.
Spring IoC コンテナには、XML 構成拡張以外にも、数多くの新機能が備わっています。
XML の拡張とともに、IoC コンテナの最も重要な特徴は、Bean のライフサイクル管理をするためのカスタムスコープを追加したことです。
1. 背景
従来から Spring には、SingletonとPrototype (つまり、非 Singleton)という 2 つの Beans 用スコープが備わっていました。
Singleton Bean は、所有するコンテナのコンテキストにある単集合です。コンテナの存続中にちょうど 1 つのインスタンスが存在し、コンテナが閉じられると、破壊イベントに関係しているすべての Singleton Bean にイベントを発行し、たとえば、プールされた接続のような管理されたリソースを閉鎖します。
Prototype Bean は、別の Bean への挿入を通じて参照される場合や、所有するコンテナに対する getBean() 呼び出しに対応して参照される場合に必ず作成されます。この場合、Bean 定義は 1 つのオブジェクトに対応しているのではなく、オブジェクトを作成するための方法です。作成された各インスタンスはまったく同じように構成されますが、別個のアイデンティティを持ちます。Spring のコンテナは、Prototype への参照に依存しません。そのライフサイクルは、それを取得したコードが責任を負います。
Spring 2.0 では、カスタムスコープの機能を搭載しました。カスタムスコープには任意の名前を付けることができます。このスコープは一般的に、オブジェクトのインスタンスを管理できる補助記憶に対応しています。この場合、Spring はよく知られているプログラミングモデルを提供するので、挿入と参照が可能になり、スコープされたオブジェクトのインスタンス管理を補助記憶が行います。
一般的な補助記憶は以下のとおりです。
2. Web スコープ
この機能に対する最も一般的な要件は、Web アプリケーションの HTTP セッションにオブジェクトをトランスペアレントに保存することに関するものです。これは、Spring 2.0 でサポートされたアウトオブボックスです。この要件は一般的であるため、例としては優れた基盤です。
以下の Bean 定義について考えてみましょう。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
ここでは、デフォルトの singleton ではなく、session スコープを指定します。スコープには任意の名前を付けられますが、Web アプリケーションで使うために、session と request がボックスから提供されます。
userPreferences の名前でgetBean() を呼び出すと、Spring は 現在の HTTP セッションからUserPreferences オブジェクトを透過的に解決します。UserPreferences オブジェクトが見つからない場合は、Spring によって作成されます。挿入によって、再設定した UserPreferences の設計図が可能になり、当該ユーザー用にカスタマイズできるようになります。
これを有効にするためには、web.xml ファイルで以下のようにフィルタを定義する必要があります。そのためには、Spring がどの HTTP セッションのオブジェクトを参照すべきなのかがわかるように、見えないところでバインドする ThreadLocal が処理されます。
<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener></listener-class>
</listener>
...
</web-app>
この場合は、ルックアップに対処しているが、通常は API を使わない挿入スタイルのほうが好ましいでしょう。"userPreferences" ライフサイクルがより長い Bean をほかの Bean に挿入したい場合はどうなるでしょうか。たとえば、以下のような Spring MVC Controller 単集合の個々の UserPreferences オブジェクトを処理したい場合はどうでしょう。
public class UserController extends AbstractController {
private UserPreferences userPreferences;
public void setUserPreferences(UserPreferences userPreferences) { this.userPreferences = userPreferences;
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// do work with this.userPreferences
// Will relate to current user }
}
この場合は、より短命の UserPreferences オブジェクトが挿入対象の使用ポイントで解決されるような効果的な「ジャストインタイム」の挿入が必要になります。
一般的に、Spring の挿入は静的であるため必ずステートレスになると誤解されていますが、これは間違いです。Spring には、高度なプロキシベースの AOP フレームワークが備わっているので、実行時にルックアップを隠して、このような「ジャストインタイム」機能を実現しています。IoC コンテナは挿入されるものを制御するため、必要な lookup を隠すプロキシを挿入することができます。
以下のように、コンテナにこのようなプロキシを実行するように簡単に指示できます。サブ要素
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<aop:scoped-proxy/>
</bean>
<!-- a singleton-scoped bean injected with a proxy to the above bean -->
<bean id="userController" class="com.mycompany.web.UserController">
<!-- a reference to the proxied 'userPreferences' bean -->
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>
これで、予想どおり解決は動的に行なわれます。UserController の userPreferences インスタンス変数は、HTTP セッションの正しい対象 UserPreferences を解決するプロキシになります。HTTP セッションと直接対話する必要がなく、模倣の HttpSession オブジェクトを使わずに簡単に UserController をユニットテストできます。
「ジャストインタイム」挿入を実現するもう 1 つの方法は、lookup メソッドを使う方法です。これは、Spring 1.1 以降で可能になった手法であり、この手法によって寿命の長い(通常は単集合)Bean を、より短命の可能性がある Bean に依存させることができます。コンテナは抽象化(または具象化)メソッドを無効にして、メソッドの呼び出し時に getBean() 呼び出しの実行結果を返すことができます。
この場合は、挿入対象のインスタンス変数ではなく、必要なオブジェクトを返す抽象化メソッドを定義します。このメソッドは、公開または保護されている可能性があります。
public abstract class UserController extends AbstractController {
protected abstract UserPreferences getUserPreferences();
@Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// do work with object returned by getUserPreferences()
// Will relate to current user }
}
この場合は、スコープされたプロキシを使う必要はありません。挿入された Bean ではなく、挿入対象の Bean 定義を変更する。XML 構成は以下のようになります。
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session" />
<!-- a singleton-scoped bean injected with a proxy to the above bean -->
<bean id="userController" class="com.mycompany.web.UserController">
<!-- a reference to the proxied 'userPreferences' bean -->
<lookup-method name="getUserPreferences" bean="userPreferences" />
</bean>
</beans>
このメカニズムでは、クラスパス上に CGLIB が必要になります。
3. その他の可能性
当然ながら、Spring 本来の手法では、基盤となるメカニズムはプラグ可能であり、Web 層と連動していません。たとえば、Tangosol と Interface21 が提供する datagrid ネーム空間を利用して、Tangosol Coherence のスコープを以下のように使うことができます。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:datagrid="http://schemas.tangosol.com/schema/datagrid-for-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://schemas.tangosol.com/schema/datagrid-for-spring
http://schemas.tangosol.com/schema/datagrid-for-spring/datagrid-for-spring.xsd">
<datagrid:member/>
<bean id="brian" class="Person" scope="datagrid">
<aop:scoped-proxy/>
<property name="firstName" value="brian" />
<property name="lastName" value="oliver" />
<property name="age" value="21" />
</bean>
</beans>
Bean が datagrid 内でスコープされていることを宣言するということは、Bean の状態管理は Coherence によって、ローカルサーバー上ではなくクラスタードキャッシュで行なわれるということです。もちろん、Bean は Spring によってインスタンス化されて挿入され、Spring のサービスの恩恵を受けることができます。スコープされた Bean は、SOA やバッチプロセスのコンテキストで役に立つことを期待しており、将来の Spring のリリースにおいて、アウトオブボックスのスコープをさらに増大させると考えています。
4. カスタムスコープ
ユーザー独自のカスタムスコープは簡単に定義できます。その方法については、リファレンスマニュアルに詳細に記載されています(http://static.springframework.org/spring/docs/2.0.x/reference/beans.html#beans-factory-scopes)。現在のスコープのオブジェクトを解決する方法を明確にする戦略が必要です。Web スコープは見えないところでこれを実行するため、一般的には ThreadLocal が必要になります。
Java 5 を実行している場合、Spring 2.0 ではジェネリクスなどの新しい機能の恩恵を受けることができます。たとえば、以下のようなクラスがあるとします。
public class DependsOnLists {
private List plainList;
private List floatList;
public List getFloatList() {
return floatList;
}
public void setFloatList(List floatList) {
this.floatList = floatList;
}
public List getPlainList() {
return plainList;
}
public void setPlainList(List plainList) {
this.plainList = plainList;
}
}
plainList プロパティは旧式のコレクションですが、floatList プロパティには型付き表示があります。
以下の Spring 構成について考えてみましょう。
<bean class="com.interface21.spring2.ioc.DependsOnLists">
<property name="plainList">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
<property name="floatList">
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</property>
</bean>
Spring は floatList プロパティに、文字列ではなくフロートを正しく挿入します。型変換を行う必要があることを理解するのに十分スマートな方法だからです。
以下のテストによって例証します。
public class GenericListTest extends
AbstractDependencyInjectionSpringContextTests {
private DependsOnLists dependsOnLists;
public void setDependsOnLists(DependsOnLists dependsOnLists) {
this.dependsOnLists = dependsOnLists;
}
@Override
protected String[] getConfigLocations() {
return new String[] { "/com/interface21/spring2/ioc/inference.xml" };
}
public void testLists() {
List plainList = dependsOnLists.getPlainList();
ListfloatList = dependsOnLists.getFloatList();
for (Object o : plainList) {
assertTrue(o instanceof String);
System.out.println("Value='" + o + "', class=" +
o.getClass().getName());
}
for (Float f : floatList) {
System.out.println("Value='" + f + "', class=" +
f.getClass().getName());
}
}
}
出力は以下のようになります。
Value='1', class=java.lang.String
Value='2', class=java.lang.String
Value='3', class=java.lang.String
Value='1.0', class=java.lang.Float
Value='2.0', class=java.lang.Float
Value='3.0', class=java.lang.Float
IoC コンテナには数多くの拡張ポイントがあります。具体的には、以下のようなポイントです。
これらのトピックは主にパワーユーザーや Spring を使って製品を記述する人たちに関連するものであるため、この記事で取り扱う内容ではありません。しかし、Spring 2.0 の強化点は表面的なことばかりではなく、目に見えない部分で多くの努力が行なわれてきたということを理解しておくことが重要です。
OSGi との統合をサポートするためのさまざまな強化が行なわれてきたため、Spring コンポーネントモデルを利用して動的なモジュール管理のパワーと OSGi を統合する Spring OSGi 統合プロジェクトが可能になりました。この作業は、Spring JAR ファイルを OSGi バンドルとしてパッケージングすることで、Spring 2.1 でも続けられます。
Spring 2.0 における最も刺激的な強化点の 1 つは Spring AOP に関するものですが、Spring AOP はより使いやすく、そしてよりパワフルになります。これは主に、純粋な Java プロキシベースのランタイムを維持しながら高度で成熟した AspectJ 言語の機能性を活用する能力によって実現するものです。
AOP (Aspect Oriented Programming: アスペクト指向プログラミング)は重要であると私たちはずっと信じてきました。その理由は、AOP はプログラム構造に関する新しい考え方を示してくれるものであり、それによって純粋な OOP では解決できなかった多くの重要な問題を解決でき、分散した方法ではなくモジュラーに特定の要件を実装できるからです。
これらのメリットを理解するために、純粋な OO コードに直接実装できないが要件で表現できることについて考えてみましょう。企業の開発担当者は、明確に伝達できる共通語彙を使います。たとえば、サービス層、DAO 層、Web 層、Web コントローラーといった用語は説明する必要がありません。
多くの要件がこのような語彙の用語で表現されます。たとえば、
これらの要件すべては現実的で経験から引き出されたものですが、純粋な OOP を使って簡潔にアドレス指定することはできません。主な理由として以下の 2 つが挙げられます。
AOP は、クロスカッティング問題をモジュール化し、プログラム対象となる抽象化として、共通語彙の用語を表現できるようにすることによって、このような問題を解決するテクノロジーです。このような抽象化はポイントカットと呼ばれているが、これについてもう少し詳しく手短に説明します。この方法の主なメリットは以下のとおりです。
このような理由で AOP は重要であり、最高のソリューションを提供したいのです。
Spring AOP は間違いなく最も広く利用されている AOP テクノロジーです。このポジションを得られたのは、以下のような強みがあるからです。
しかし、Spring 2.0 より前は、Spring の AOP には以下のようないくつかの欠点がありました。
Spring 2.0 のこの重要な分野を強化するために、私たちはその強みを基盤として構築しながら、弱点に対応したいと考えました。
最初の 2 つの弱点は最も重要です。どちらもポイントカットに関連するものです。最後の 3 つは、実際には Spring ユーザーにはあまり発生しないものですが、問題があることが判明した場合は、AspectJ の使用をお勧めします(おわかりのとおり、これは現在、Spring AOP からの単純明快な進歩です)。
XML 構成拡張によって、重要課題の 1 つが解決できました。Spring モジュラーの設計は変えたくなかったので、これまでは Spring DTD の AOP に固有のタグを提供できませんでした。そのため、このようなケースでは冗長になる可能性がある汎用構成に依拠する必要がありました。Spring 2.0 では、この問題は解消されました。DTD とは異なり、XML スキーマは拡張が可能だからです。私たちは、IoC コンテナに AOP 構造物を認識させると思われる AOP ネーム空間を、モジュラー性を損なうことなく提供することができました。
AOP 関連の用語の修正を簡単にしきましょう。Spring AOP を使ったことがある人にとって、これらの概念はなじみ深いものでしょう。概念はまったく同じです。単に、別のそして簡潔でパワフルな表現方法であるいうことだけの違いです。
ポイントカットは突き合わせルールであり、アスペクトを適用すべきプログラムの実行における一連のポイントを定義するものです。このようなポイントは、ジョインポイントと呼ばれます。アプリケーションの実行にともなって、オブジェクトのインスタンシエーションやメソッドの呼び出しなどのジョインポイントが接近します。Spring AOP(すべてのバージョン)の場合、サポートされている唯一のジョインポイントは公開メソッドの実行です。
アドバイスは、アスペクトがジョインポイントに適用する挙動です。アドバイスはジョインポイントの前後に適用できます。アドバイスの全種類は以下のとおりです。
アスペクトは、ポイントカットとアドバイスを結合し、特定のクロスカッティング問題に対応するモジュラーソリューションを作成します。
現時点では、少し抽象的に思えたとしても心配する必要はありません。コードの例をみれば、すぐにすべてが明確になるでしょう。
Spring 2.0 および AspectJ のコンテキストにおける AOP の基本に関するもっと詳しい説明については、infoq の Adrian のすばらしい記事「Spring 2.0 と AspectJ で企業アプリケーションを簡素化する」 (source)を参照してください。
これまで論じてきた概念は、Spring AOP や AspectJ に固有のものではなく、AOP の基本的な概念であり、Spring 1.x ですでにあった概念です。それでは、なぜ私たちは Spring 2.0 で、AspectJ との連係を選んだのでしょうか。
もしポイントカット表現言語が必要なのであれば、選択は簡単です。AspectJ には、よく考えられ、厳格に定義され、うまくドキュメント化されたポイントカット言語が備わっています。最近、総合的な全面改訂を行ない、Java 5 での実行時に Java 5 シンタックスを利用できるようにしました。専用の優れた参考資料はありませんが、これについて説明している書籍や記事は数多くあります。
私たちは、一からやり直す必要はない(無駄な作業の重複は避けるべき)と考えており、独自のポイントカット表現言語を定めることの正当性を認められませんでした。さらに、2005 年の初めの AspectWerkz を AspectJ に統合するプロジェクト以来、AspectJ は Spring 2.0 以外では、唯一の主流 AOP テクノロジーであることは明らかです。したがって、臨界質量が重要ポイントであり、技術的な優秀さでした。
新しい AOP ネーム空間のおかげで、Spring XML 構成によって AspectJ ポイントカット表現を指定できるようになり、ポイントカットでマッチングされたメソッドを使って任意の Spring Bean へのアドバイスを対象とすることができます。
前述の Person クラスについて考えてみましょう。このクラスには年齢プロパティだけでなく誕生日メソッドがあり、それによってそのプロパティを増加させています。
public void birthday() {
++age;
}
誕生日メソッドが呼び出された場合は、該当する人に必ずバースデーカードを送らなければならないといった要件があるとしましょう。クロスカッティングの要件として典型的な例があります。それは、私たちの基幹のビジネスロジックとは別の問題です。理想的には、Person オブジェクトに影響を与えずにその機能をモジュール化したいと考えています。
今度は、アドバイスについて考えてみましょう。もちろん、実際にバースデーカードを送ることだけでなく E カードを送ることがこのメソッドの主な役割となります。しかし、この記事の目的を考えると、私たちはバースデーカードを送る手順ではなく、トリガーインフラに興味があります。したがって、私たちは単にコンソール出力を利用します。このメソッドでは、今日が誕生日である Person へのアクセスが必要であり、誕生日メソッドが呼び出された場合は、必ず呼び出されるようにしたいと考えます。私たちのシンプルなアドバイス実装は以下のとおりです。
public class BirthdayCardSender {
public void onBirthday(Person person) {
System.out.println("I will send a birthday card to " +
person.getName() + "; he has just turned "
person.getAge());
}
}
基本的には、観察可能になる Person に変更を加えることなく、Person 上に Observer メカニズムのようなものを構築します。また、このケースでは、アスペクトとしてBirthdayCardSender オブジェクトを使いますが、フレームワークに固有のインターフェイスを実装する必要は一切ないことに注意してください。これによって、既存のクラスをアスペクトして使う可能性が実現し、Spring の非侵入的なプログラミングモデルをさらに拡張して、潜在的なアスペクトや正規のクラスを包括できるようになります。
Spring 2.0 では、以下のように BirthdayCardSender をアスペクトとして使うことができます。まず、BirthdayCardSender クラスを Bean として定義します。この作業は非常に簡単です。
<bean id="birthdayCardSenderAspect"
class="com.interface21.spring2.aop.BirthdayCardSender" />
私たちは、このオブジェクトを依存挿入したり、必要に応じて任意の Spring コンポーネントモデルサービスを適用したりすることができました。
次に、AOP 構成を追加して、Spring が管理する Person に対して birthday() メソッドが呼び出されたら、BirthdayCardSender Bean の onBirthday() メソッドを必ず呼び出すように Spring に指示をします。これは、新しい
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
次に、以下のように新しい AOP タグを使います。これは、このアスペクトを適用する完全な構成です。
<aop:config>
<aop:aspect id="sendBirthdayCard" ref="birthdayCardSenderAspect">
<aop:after
method="onBirthday"
pointcut="execution(void com.interface21..Person.birthday()) and
this(person)"
/>
</aop:aspect>
</aop:config>
ポイントカット表現が鍵です。ポイントカット表現についてもう少し詳しく見てみましょう。
execution(void com.interface21..Person.birthday()) and this(person)
接頭辞 execution() は、メソッドの実行の突き合わせを行っていることを表します。つまり、メソッドの動作を変更しているということです。
execution() 句の内容は、突き合わせ対象となるメソッドを定義します。ここで、さまざまなクラスに対して、一連のメソッドと一致した表現を記述することもできます(実際に、それがずっと一般的であり、より価値が高い使い方です。アドバイスがただ 1 つのメソッドと対応している場合は、それを具体化することは重要性が低いのです)。最後に、呼び出し中のオブジェクトを onBirthday() メソッドの引数にバインドします。これによってポイントカットが狭められ、Person オブジェクトに対する実行のみに対応できるようになり、ルックアップを一切必要とすることなく、呼び出されたオブジェクトに到達する簡潔な方法が実現しています。私たちは、引数のバインドを利用して、メソッドのパラメータをバインドしたり、必要に応じて型、例外、ターゲットを返したりすることができます。lookup コードの除去はなじみの深いものでしょう。これは、アドバイス対象の型への依存を効果的に挿入する方法です。Spring の考え方では、API を無くして、アドバイスメソッドにユニットテストを簡単に行えるようにします。
現在では、Spring のコンテキストで定義されているすべての Person は、ポイントカット表現が 1 つまたは複数のメソッドに対応していれば、自動的にプロキシ処理されます。このポイントカットに対する対応を有しているクラスを持つ Bean は影響を受けません。同様に、Spring コンテナによってインスタンス化されていないポイントカットによって突き合わせされたオブジェクトも影響を受けません。このアスペクト構成は、追加の XML ファイルにおいても、同一のファイルにおいても、上述の IoC の例で示した既存の Bean 構成に追加できるようになっています。念のため、Person は以下のように定義します。
<bean class="com.interface21.spring2.ioc.Person"
p:name="Tony"
p:age="53"
p:house-ref="number10"
/>
構成によって、Person やその他の Bean 定義をアドバイスに適切なものにする必要はありません。Spring コンテキストで構成された Person に対して birthday() メソッドを呼び出す場合、出力は以下のようになります。
I will send a birthday card to Tony; he has just turned 54
(訳:私はトニーにバースデーカードを送る。トニーは 54 歳になったばかりである。)
アドバイスは必ず Java メソッドに格納されます。しかし、ここまでは Spring XML で定義されたポイントカットを見てきました。
アドバイスがメソッドや Java 5 コメントのポイントカットに格納されている場合、AspectJ はアスペクトを定義する簡潔な方法を実現しています。ポイントカット表現言語は、AspectJ 独自のシンタックスにおける言語と同じであり、セマンティックスもまったく同じです。しかし、@AspectJ と呼ばれるこのスタイルでは、アスペクトは javac を使ってコンパイルされます。
@AspectJシンタックスでは、フレームワークに固有のコメントは、ビジネスロジックではなくアスペクトにあります。ビジネスロジックにコメントを導入してアスペクトを動かす必要はありません。
このコメントを主体としたプログラミングモデルは AspectWerkz が始めたものであり、2005 年の初めに AspectJ プロジェクトに統合されました。AspectJ 自体では、@AspectJ アスペクトは、ロード時のウィービングによって適用されます。クラスローダーフックによって、ロードされているクラスのバイトコードに変更が加えられ、必要に応じてアスペクトを適用します。AspectJ コンパイラもこのようなアスペクトを認識するので、実装戦略を選択することができます。
Spring 2.0 には @AspectJ 用のオプションが追加されています。Spring は独自のプロキシベースの AOP ランタイムを利用して、このようなアスペクトを Spring Bean に適用できます。
このスタイルを利用すると、前述の例において同じ機能をどのように実装できるのかを見てみましょう。
アスペクトクラスには、BirthdayCardSender クラスと同じアドバイスメソッドの本文が格納されていますが、それをアスペクトとして認識するために、org.aspectj.lang.annotation.Aspect のコメントが付けられています。同じパッケージのそれ以上のコメントによって、アドバイスメソッドが定義されます。
@Aspect
public class AnnotatedBirthdayCardSender {
@After("execution(void com.interface21..Person.birthday()) and this(person)")
public void onBirthday(Person person) {
System.out.println("I will send a birthday card to " +
person.getName() + "; he has just turned " +
person.getAge());
}
}
したがって、ポイントカットとアドバイスメソッドが結合され、アスペクトが完全なモジュールになります。すべての AspectJ アスペクトと同様、@AspectJ アスペクトには、任意の数のポイントカットとアドバイスメソッドを格納できます。
Spring XML では、これを再度 Bean と定義して、別のタグを追加することによってアスペクトが自動的に適用されるようにします。
<aop:aspectj-autoproxy />
<bean id="birthdayCardSenderAspect"
class="com.interface21.spring2.aop.AnnotatedBirthdayCardSender" />
aspectj-autoproxy タグは、@AspectJ アスペクトを自動的に認識するように Spring に指示し、そのポイントカットが対応する同じコンテキストのすべての Bean に適用するタグです。
XML とコメントでは、どちらのほうがよい方法でしょうか。まず、コメントはコアビジネスロジックではなくアスペクトに格納されるので、切り替えコストは高くありません。通常は、Java コードのポイントカット表現をすべて具体化することに意味があるかどうかによって決定します。
自分のアスペクトがドメインに固有のものである場合は、コメントスタイルを検討するのがよいでしょう。つまり、ポイントカットとアドバイスは密接に関係しており、アドバイスは汎用的なものではなく、さまざまな状況で繰り返し使われる傾向があるということです。たとえば、バースデーカードの送信は、特定のアプリケーションクラス(Person)に固有のものであるため、コメントスタイルが適している用途の好例です。しかし、パフォーマンスをモニターするアスペクトは、異なるアプリケーションでは異なる使い方をされる可能性があり、アドバイスメソッドはポイントカットと分離して、ポイントカットが XML 構成の中でもっと自然に存在できるようにするのがいちばんよいのかもしれません。
以下の場合には XML を使います。
@AspectJ アスペクトを利用して、以下のようにプログラムで AOP プロキシを作成することも可能です。
Person tony = new Person();
tony.setName("Tony");
tony.setAge(53);
AspectJProxyFactory ajpf = new AspectJProxyFactory(tony);
ajpf.addAspect(new AnnotatedBirthdayCardSender());
Person proxy = ajpf.getProxy();
AnnotatedBirthdayCardSender は自動的に @AspectJ アスペクトとして認識され、プロキシが定義する動作によって、プロキシはターゲットを装飾します。このスタイルの場合、Spring IoC コンテナは必要とされません。
プログラムによるプロキシの作成は、インフラのコードやテストを記述する際には便利ですが、正規のビジネスアプリケーションではあまり使用されません。
これまで見てきたことは、表面的なことに過ぎません。
AspectJ ポイントカット表現、Spring XML、@AspectJ スタイル (または、もちろん、AspectJ 言語そのもの)のもっと高度な機能のいくつかを見てみましょう。
ターゲットのバインドについてはすでに論じた。パラメータのバインドの例を挙げてみましょう。:
@Aspect
public class ParameterCaptureAspect {
@Before("execution(* *.*(String, ..)) && args(s)")
public void logStringArg(String s) {
System.out.println("String arg was '" + s + "'");
}
}
args() 句は、突き合わせが行なわれているメソッドの引数に対して拘束力があり、String タイプの最初の引数を有するメソッドに暗黙的に狭めらます。ポイントカットによって最初の引数(String 型の引数でなければならない)がバインドされるため、アドバイスメソッドではキャスティングは必要ありません。
safety 型は当然このメカニズムからの流れです。このポイントカットでターゲットとされたアドバイスは、間違った型の引数や対応しない引数によって不適切に呼び出されることは絶対にありません。
このメカニズムが優れていることの例を示すために、Spring 1.x MethodBeforeAdvice においてメカニズムがどのようになっているかを以下に示します。AOP Alliance MethodInterceptor(Spring 1.x アプリケーションに実装されている最も一般的なインターフェイス)の場合と同様、さまざまな引数を調べて、目的の引数を見つける必要があります。
public class ParameterCaptureInterceptor implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
if (args.length >= 1 && method.getParameterTypes()[0] == String.class) {
String s = (String) args[0];
System.out.println("String arg was '" + s + "'");
}
}
}
Spring AOP Pointcut を使って、MethodBeforeAdvice のガードを取り除くこともできますが、そのためにはJava コードを記述する必要があるでしょう。インターセプターにガードを設定するとポイントカットを利用するよりも遅くなります。それは、AOP ランタイムによって、絶対に呼び出されないアドバイスを最適化することができないからです。
ここでは、真の AOP をどの程度傍受から排除できますか、そして、それがよりシンプルかつパワフルな理由を確認できます。もう一度言いますが、EJB 3.0 の傍受は Spring の最初の AOP 機能に比べて格段に劣ります。これは、本当のポイントカットメカニズムが欠如しているからです。つまり、ClassCastException と ArrayIndexOutOfBoundsException のどちらもリスクになることが予想されるということです。EJB 3.0 では専用のアドバイス型が使えないため、Before アドバイスではなく Around アドバイス(インターセプター)を使う必要があるでしょう。また、InvocationContext オブジェクトを提供する必要があるため、アドバイスメソッドをユニットテストすることがより困難になります。
AspectJ ポイントカット表現言語の能力は、高度な構造物を構築できることだけではありません。エラーの可能性やアプリケーションの堅牢性を増すという点でも重要な役割を担っています。また、アドバイスメソッドにおいてガードコードの必要性をなくすことよって、必要なコードの量が大幅に減ります。
構成と再利用が本当の言語の特性です。AspectJ の主な目的は、ポイントカット表現においてこのような特性を実現することです。
実際のポイントカットの再利用について考えてみましょう。
@AspectJシンタックス(たとえば、Spring 2.0 AOP XML フォーマット)を使うと、名前付きのポイントカットを定義できます。@AspectJ シンタックスでは、無効なメソッドに対して @Pointcut コメントを以下のように使います。
@Pointcut("execution(public !void get*())")
public void getter() {}
@Pointcut コメントを使うとポイントカット表現を定義することができ、必要に応じて引数の数や型をポイントカットによってバインドすることができます。メソッドの名前は、ポイントカットの名前として使われます。
上述のポイントカットは、JavaBean のゲッターの突き合わせを行います。この突き合わせの強みについて注目してください。私たちは、単にワイルドカードではなく言語セマンティックスに取り組んでいます。稚拙な手法では、名前が get で始まるすべてのメソッドがゲッターとみなされます。このポイントカットは格段に堅牢です。ゲッターが公開されていて、無効でない戻り(!void)がなく、引数がない(引数の括弧が空であることでわかる)ことを示しています。
ポイントカットを追加して、int を返すメソッドの突き合わせを行ってみましょう。
@Pointcut("execution(public int *())")
public void methodReturningInt() {}
これで、これらのポイントカットの観点でアドバイスを表現できるようになりました。最初のサンプルでは、単に最初の名前付きポイントカット getter を参照するだけです。
@After("getter()")
public void getterCalled(JoinPoint jp) {
System.out.println("Method " + jp.getSignature().getName() +
" is a getter");
}
しかし、事態はこれからおもしろくなります。ここで、2 つのポイントカットを 1 つの表現に AND 処理することによって、int を返すゲッターにアドバイスを適用します。
@After("getter() and methodReturningInt()")
public void getterCalledThatReturnsInt(JoinPoint jp) {
System.out.println("ANDing of pointcuts: Method " +
jp.getSignature().getName() +
" is a getter that also returns int");
}
AND 処理とは、両方のポイントカットが適用されなければならないことを意味します。OR 処理とは、1 つまたはその他が適用されなければならないことを意味します。私たちは、必要とされるどのような複雑さの表現でも構築できます。
AND 処理と OR 処理の両方を、以下の完全なアスペクトで実証します。
@Aspect public class PointcutReuse {
@Pointcut("execution(public !void get*())" )
public void getter() {}
@Pointcut("execution(public int *())" )
public void methodReturningInt() {}
@Pointcut("execution(public void *(..))" )
public void voidMethod() {}
@Pointcut("execution(public * *())" )
public void takesNoArgs() {}
@After("methodReturningInt()" )
public void returnedInt(JoinPoint jp) {
System.out .println("Method " + jp.getSignature().getName() +
" returned int" );
}
@After("getter()")
public void getterCalled(JoinPoint jp) {
System.out .println("Method " + jp.getSignature().getName() +
" is a getter" );
}
@After("getter() and methodReturningInt()" )
public void getterCalledThatReturnsInt(JoinPoint jp) {
System.out.println("ANDing of pointcuts: Method " +
jp.getSignature().getName() +
" is a getter that also returns int");
}
@After("getter() or voidMethod()" )
public void getterOrVoidMethodCalled(JoinPoint jp) {
System.out .println("ORing of pointcuts: Method " +
jp.getSignature().getName() +
" is a getter OR is void" );
}
}
出力は以下のようになり、ポイントカット表現の OR 処理と AND 処理が示されます。
Method getName is a getter
ORing of pointcuts: Method getName is a getter OR is void
ORing of pointcuts: Method birthday is a getter OR is void
Method getName is a getter
ORing of pointcuts: Method getName is a getter OR is void
Method getAge returned int
Method getAge is a getter
ANDing of pointcuts: Method getAge is a getter that also returns int
ORing of pointcuts: Method getAge is a getter OR is void
I will send a birthday card to Tony; he has just turned 54
ポイントカット構成は Spring AOP XML でも実現できます。その場合は、演算子 "&&" と "||" の代わりに and と or を使って、XML 属性値の問題を回避します。
AspectJ 言語そのもので記述された AspectJ ポイントカット表現を再利用して、JAR ファイルにコンパイルすることができます。Eclipse を使うと、AJDT プラグインを使ってこのようなアスペクトを作成することができます。または、すでに AspectJ を使っている場合は、すでにこのようなアスペクトが存在している可能性があるので、そのアスペクトを使うことをお勧めします。
証明として、前述の例の一部を書き直し、AspectJ アスペクトにポイントカットを挿入してみます。
public aspect LibraryAspect {
pointcut getter() :
execution(public !void get*());
...
}
このアスペクトは Aspect 言語で記述されているため、AspectJ ajc コンパイラでコンパイルする必要があります。アスペクトとポイントカット用のキーボードを使えることに注意してください。
Spring で使用する @AspectJ アスペクトのこのアスペクトは以下のように参照できます。通常はクラスパス上の JAR ファイルにパッケージされたアスペクトの FQN を使うことができることに注意してください。
@Aspect
public class PointcutReuse {
@After("mycompany.mypackage.LibraryAspect.getter()")
public void getterCalled(JoinPoint jp) {
System.out.println("Method " + jp.getSignature().getName() +
" is a getter");
}
また、このクラスは javac を使ってコンパイルし、Spring で適用することができます。
Spring XML でも AspectJ アスペクトは参照できます。ご存知のとおり、Spring 2.0 と AspectJ は非常に密接に統合されていますが、Spring AOP では、AspectJ コンパイラやウィーバを使わなくても完全なランタイムを実現しています。
非常に複雑なポイントカット表現がある場合は、AspectJ 言語とツールサポートが必須の場合と同様、AspectJ ライブラリアスペクトを使うのがベストです。
Spring ユーザーにとってはどのような意味があるのでしょうか?
AOP によって企業ソフトウェアの重要な問題が解決できることを認めてもらい、傍受や AOP の代替品と比べて、AspectJ プログラミングモデルがいかにパワフルで簡潔かを理解頂きたいのです。
既存の Spring MethodInterceptor やその他のアドバイス実装を新しいプログラミングモデルに移行する必要はありません。そのままでも十分に機能します。しかし、今後は新しいプログラミングモデルを使うことが好ましく、格段に強制的になります。
ProxyFactoryBean または TransactionProxyFactoryBean を使って同時にプロキシを構成している場合は、自動プロキシ処理(Spring 2.0 では、より簡単かつより当たり前になる)によって Spring 構成のボリュームが格段に小さくなり、チームのメンバー間で作業を分割がスムーズになります。
用法モデルは異なるように見えますが、概念(ジョインポイント、ポイントカット、アドバイス)は、Spring AOP や Spring で 2003 年以来実装されてきた AOP Alliance API の概念とまったく同じであることを記憶に留めておいてください。Spring AOP には常にこのような概念に対応したインターフェイスが備わっています。
おそらくもっと驚かれるであろうことは、実装は実際にはカバーの下では、まったく同じであるということです。AspectJ 構造物を活用する新しいプログラミングモデルは、既存の Spring AOP ランタイムを基盤に構築されています。Spring AOP は常に高い柔軟性を有していたため、このことで大きな変化は生じません(Spring 1.x と同様、org.springframework.aop.framework.Advised インターフェイスを使って、今でも AOP プロキシの状態を問い合わせたり、変更を加えたりすることができます)。
つまり、AOP Alliance と Spring AOP アスペクトを AspectJ スタイルのアスペクトと突き合わせして、それらを組み合わせることができるということです。これは、既存のアスペクトを活用したい場合は、特に重要です。
プログラムによるプロキシの作成の例を増やして、これを証明してみましょう。従来の Spring AOP スタイル MethodInterceptor を追加します。
Person tony = new Person();
tony.setName("Tony");
tony.setAge(53);
AspectJProxyFactory ajpf = new AspectJProxyFactory(tony);
ajpf.addAspect(new AnnotatedBirthdayCardSender());
Person proxy = ajpf.getProxy();
ajpf.addAdvice(new MethodInterceptor() {
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("MethodInterceptor: Call to " + mi.getMethod());
return mi.proceed();
}
});
出力は以下のようになり、MethodInterceptor(ポイントカットがなければ、すべてのメソッドの呼び出しに対応する)からの出力が @AspectJ スタイルで記述された BirthdayCardSender の出力に挿入されます。
MethodInterceptor: Call to public void
com.interface21.spring2.ioc.Person.birthday()
MethodInterceptor: Call to public java.lang.String
com.interface21.spring2.ioc.Person.getName()
MethodInterceptor: Call to public int
com.interface21.spring2.ioc.Person.getAge()
I will send a birthday card to Tony; he has just turned 54
Spring 2.0 によって、AOP の世界に新しくて望ましい統一が実現します。アスペクトの実装が運用モデルから初めて独立するようになりました。これまで見てきた @AspectJ の例のすべてが AspectJ コンパイラを使ってコンパイルしたり、AspectJ ロードタイムウィービングを使って適用したりすることができるだけでなく、Spring によって適用されるようになります。Spring の考え方では、さまざまなランタイム状況をカバーするプログラミングモデルを 1 つ有しています。
そして、AspectJ 自体を採用したい場合は、AspectJ ポイントカット表現の概念を幅広い層にもたらす Spring によって、自然な進展が起こります。
いつ AspectJ を使うべきなのか。以下にガイドラインを示します。
必ずしも、「どちらか一方」の選択ではありません。AspectJ と Spring AOP を同時に使うことは可能です。両者は競合しないのです。
Spring 2.0 は、Java 1.3 や 1.4 との下位互換性を保っています。しかし、Java 5 をターゲットにしている新しい機能の数は増えています。
タイプインターフェイスなど、そのような新機能の中には、すでに説明したように、無料のものもあります。また、自分で選択しなければならないものもあります。いくつかを簡単に見てみましょう。
さまざまな新しい API が、Java の初期バージョンで動作し続ける中核機能に基づいて、Java 5 の機能を備えています。
特に以下の機能が挙げられます。
このようなクラスの数は、次第に増えていくでしょう。
SimpleJdbcTemplate が例証となります。集約関数の呼び出しに対する影響について見てみましょう。Java 5 よりも前の JdbcTemplate を使って、以下のようにバインド変数をアレイにラップする必要があります。
jdbcTemplate.queryForInt("SELECT COUNT(0) FROM T_CLIENT WHERE TYPE=? AND CURRENCY=?",
new Object[] { new Integer(13), "GBP" }
);
Java 5 を使えば、基本ラッパー型が必要なくなるので、オートボックス処理によって多少のノイズを取り除くことができます。これは、Spring が新しいものを提供しなければならないことではなく、単に言語の特徴に起因しています。
jdbcTemplate.queryForInt("SELECT COUNT(0) FROM T_CLIENT WHERE TYPE=? AND CURRENCY=?",
new Object[] { 13, "GBP" }
);
しかし、Java 5 を採用することで、オブジェクトアレイ全体の必要性をなくすことができます。以下の例は、SimpleJdbcTemplate がバインド変数の varargs をどのように使うかを示しています。つまり、開発担当者は、アレイなしで任意の数の varargs を提供できるということです。
simpleJdbcTemplate.queryForInt("SELECT COUNT(0) FROM T_CLIENT WHERE TYPE=? AND CURRENCY=?",
13, "GBP"
);
付加的なメリットとして、バインド変数があるケースとないケースを区別する必要がなくなったということが挙げられます。そのためには、JdbcTemplate に対して 2 つのメソッドが必要になったが、リテラル SQL を取るオーバーロードされたメソッドに、空の Object アレイを渡す必要性をなくすために、SimpleJdbcTemplate を使って、フレームワークコードが varargs の長さをチェックすることができるようになりました。次の例は同じメソッドを呼び出しています。
simpleJdbcTemplate.queryForInt("SELECT COUNT(0) FROM T_CLIENT");
もっと重要なメリットもあります。ジェネリクスによって、署名が明確になり、キャストがなくなります。たとえば、JdbcTemplate に対するqueryForMap() メソッドは、ResultSet のカラム名からカラム値に Map を返す。SimpleJdbcTemplate に対するメソッドの署名が明示的な部分であれば、これはもっと明確になります。
public Map<String, Object> queryForMap(String sql, Object... args)
throws DataAccessException
このようなマップのリストを返すメソッドで、これはさらに明確になります。
public List<Map<String, Object>> queryForList(String sql, Object ... args)
throws DataAccessException
SimpleJdbcTemplate の付加的な目的は、もっとも頻繁に使われるようなメソッドのみを提供することです。JdbcTemplate は、Spring で最も大きなクラスの 1 つであり、多くのメソッドを有しています。これらのメソッドの一部には深遠な目的がある。このような高度なケースでは、総合的な問題の複雑さという観点で、言語シンタックスシュガーの重要度は低くなりがちです。このような場合は、getJdbcOperations() メソッドでアクセスできる JdbcTemplate インスタンスを SimpleJdbcTemplate がラップします。
DAO サポートクラスの使い方モデルをサポートするために、Spring 2.0 には SimpleJdbcDaoSupport クラスが備わっており、これによって JdbcTemplate を事前構成しています。また、SimpleJdbcTemplate をインスタンス化したり、直接挿入したりするのは JdbcTemplate と同じくらい簡単であり、単にリレーショナルデータアクセスに対する Spring のすべてのサポートの開始点である javax.sql.Datasource の実装を実現しているだけです。JdbcTemplate と同様、SimpleJdbcTemplate は Spring を別の方法で使うことなくクラスライブラリとして使えます。
Spring 1.2 で、まずアノテーションをオプションの機能として導入し、徐々に拡張してきました。
AspectJ から AOP のアノテーションを利用することについてはすでに述べました。これらは、シングルコードモジュールで、ポイントカットやアドバイスを簡潔に表現する方法を実現しています。
Spring には、トランザクション管理といった分野の数多くの独自のコメントも備えています。1.2 では以下が導入されました。
以下に、2.0 の最も重要な新しいアノテーションを示します。
新しい AbstractJpaTest や汎用スーパークラス AbstractAnnotationAwareTransactionalTests といった Spring の JPA テスト用の統合テストスーパークラスは、@Repeat(テストを繰り返す)や @ExpectedException(テストは特定の例外を投じる必要があり、そうしなければ失敗することを示す)といったアノテーションをサポートするようになりました。残念ながら、JUnit 3 は具体的な継承に基づいて設計されているため、このような便利なアノテーションは、Spring を使ったほかのテストでは利用できません。JUnit 4 の巻き取りが増えるにつれて、この機能をほかのユーザーに開放することができる統合テストバージョンを提供します。
自分のアノテーションを解釈したい場合はどうすればよいのでしょうか。このようなケースでは、もちろん Spring のさまざまな拡張フックが役に立ちます。たとえば、RequiredAnnotationBeanPostProcessor 作業と同じ方法で、特定のアノテーションでメソッドを識別する BeanPostProcessor を記述することもできます。近日発売予定の WebLogic 10 で使われている Pitchfork プロジェクト(http://www.interface21.com/pitchfork)では、これらの拡張ポイントを利用して、Spring の上に JSR-250 アノテーションと EJB 3.0 インターセプトアノテーションを実装しています。
また、Spring 2.0 に備わった AspectJ ポイントカット表現言語には、きわめて強力なアノテーション突き合わせ機能が備わっていることは注目に値します。アノテーションをターゲットとしたポイントカットは簡単に記述できます。たとえば、以下のポイントカット表現は、Spring フレームワークパッケージのアノテーションが付けられたすべてのメソッドの突き合わせを行います。
execution(@(org.springframework..*) * *(..))
以下の表現は、@Transactional アノテーションが付けられたすべてのクラスの突き合わせを行います。
@within(org.springframework.transaction.annotation.Transactional)
AspectJ 5 は AspectJ 言語を大きく拡張したものであり、基盤となる言語の進化に遅れないようにするための技術的なすばらしい取り組みでした。また、ポイントカット表現も、ジェネリクスや varargs といったその他の Java 5 構造物の突き合わせも行うことができます。
このシリーズの次の記事では、以下の項目を取り上げます。
ところで、今日私が説明した内容をもっと詳しく知りたい方には、以下のリソースをお勧めします。
この記事に付属するサンプルコードは、ここ(zip)からダウンロードできます。
原文はこちらです:http://www.infoq.com/articles/spring-2-intro
(このArticleは2007年1月15日にリリースされました)
前回はMicrosoft Web Platform Installerを利用して、DotNetNukeとWebMatrixをインストールする方法を紹介した。今回は、DotNetNukeのインストール方法を紹介する。
DotNetNukeは、Windows Serverで動作するCMS(Contents Management System)である。この記事ではWeb Platform Installer を利用して人気CMS「DotNetNuke」と無償Web開発環境「WebMatrix」のインストールする方法を紹介する。
クラウドコンピューティングを前提とした大規模データ技術が利用可能となってきています。Big Dataが一過性のブームで終わるかどうかにかかわらず、スケーラブルな分散アーキテクチャーの基盤はデータベース技術に主導されつつあります。RDBとORM主体のエンタープライズシステムは、HadoopやNoSQLとの組み合わせにより複合的なデータモデルに発展しました。
2011年12月8日~2011年12月9日に、ロンドンのSkills Matter eXchangeにて開催された「Groovy & Grails eXchange 2011」の参加報告を、日本Grails/Groovyユーザーグループのメンバーが3回に渡って紹介します。
Googleのクラウド環境をつかったGoogle App Engineによる開発するにあたり、初めての試みで苦悩する開発者達の経験をもとに、各開発フェーズにあわせて問題点やどう解決したかをご紹介します
去る1月12日、定理証明支援系ツールCoqの初心者向けチュートリアルが開催さ れた(http://kokucheese.com/event/index/23667/)。今後も2月2日 (http://kokucheese.com/event/index/23744/)、2月9日、2月16日と引き続���開 催されていく予定である。本記事では、開催の様子をレポートする。
Neal Gafter氏はOracleによるJava買収の影響に関する議論、Javaにセグメンテッドスタックやメタオブジェクトプロトコルを追加することについての主張、そしてJavaとC#との比較について話をしてくれた。
No comments
スレッド表示 返信