リンク
http://s2container.seasar.org/2.4/ja/
http://d.hatena.ne.jp/yukung/20100918/1284831067
Seasar2は公式サイトが簡潔にまとまっているので、公式サイトを見るだけで
だいたいわかります。
軽量コンテナの理由
WebアプリケーションはJava+JSPオンリーやPHPのみでも可能。
しかし、業務アプリとなると、
- リソース・プーリング
- データベース・アクセス
- トランザクション管理
- セキュリティ管理
などが必須となってくる。
DIコンテナやEJBコンテナはこれらの機能を提供する。
EJBでは、取り決めが厳しいためコンポーネントの開発が複雑になる。
またEJBにはアプリケーションサーバー全体を把握する必要があったり、
ユニットテストがしづらいといった問題があった。
DIとAOPを使うことによりPOJOクラスでコンポーネントが書けるようになる。
DIコンテナとして有名なものはSpring、Seasarがある。
SpringなどはXMLですべて設定するため、XMLが大量になってしまう。
Seasar2はできる限りXMLをなくしてしまおう、という発想のため、XMLは最低限になっている。
(クラス名、プロパティ名を適切に設定すればXML設定なしでAOPが実現できるなど)
AOP
AOPのしくみ
http://d.hatena.ne.jp/gnarl/20090326/1238071350
クラスを生成する際に直接newせず、AopProxyを通すことでフックさせているだけ。
プログラム中で直接newしているようなものはAOPできない。
対象クラスはSeasar2の設定により自動的にnewされたものだけ。
http://www.atmarkit.co.jp/fjava/rensai3/seasar2_02/seasar2_02_3.html
S2Container
DI/AOPするためにコンポーネントを登録するWebコンテナ。
このクラスのインスタンス.getComponent("key")で各コンポーネントを取得する。
- S2ContainerServlet
- Webアプリの場合。
web.xmlに以下を記述しておくことで、自動的にSingletonS2ContainerFactory.init()が呼ばれる。<servlet> <servlet-name>s2servlet</servlet-name> <servlet-class>org.seasar.framework.container.servlet.S2ContainerServlet</servlet-class> <init-param> <param-name>configPath</param-name> <param-value>app.dicon</param-value> </init-param>http://localhost:8080/xxx/s2servlet?command=restart
にアクセスし、稼動中にS2Containerを再起動することもできる。
- SingletonS2ContainerFactory
- Webアプリ以外の場合。
SingletonS2ContainerFactory.init();
で初期化。S2Container container = SingletonS2ContainerFactory.getContainer();
でコンテナ取得。
- S2ContainerFactory
- 自分で管理する場合。通常は使わない。
クラス
- Interceptor
- プログラム中に挿入するクラス。Adviceとも呼ぶ。
- Pointcut
- 注入するメソッドのこと
- InterceptorChain
- 複数のInterceptorをグルーピング化し、再利用しやすくする。
<component name="chain" class="org.seasar.framework.aop.interceptors.InterceptorChain"> <initMethod name="add"><arg>interceptor1</arg></initMethod> <initMethod name="add"><arg>interceptor2</arg></initMethod> <initMethod name="add"><arg>interceptor3</arg></initMethod> </component> <component ...> <aspect>chain</aspect> </component>
- AutoRegistor
- 明示的に指定せずにファイル命名規則にしたがい自動的にAOPを適用させる。
- FileSystemComponentAutoRegister
- コンポーネントの登録も自動化してしまおうという、という機能。
アスペクトの指定方法
<component>~</component>
間に<aspect>タグを記述
独自アスペクト
AbstractInterceptorを継承して作成
MethodInterceptorを継承して作成
のどちらか。
invokeメソッドに実装する。
Object invoke( MethodInvocation mi )メソッドの中で、
Object result = mi.proceed();
としてポイントカットのメソッドを実行する。
この前後処理を記述することでアスペクトする。
AbstractInterceptor.getTargetClass()
で、 アスペクトを適用する前のクラスを知ることができます。
MethodInvocationのgetThis()、getMethod()、getArguments()
で、 対象となるオブジェクト、メソッド、引数を取得できます。
アスペクト例
http://s2container.seasar.org/2.3/ja/aop.html#OriginalInterceptorSample
S2標準のインターセプター
| TraceInterceptor | メソッドの実行をログに出力するトレース処理を行う |
| ThrowsInterceptor | これを継承して例外発生時の処理を記述する |
| TraceThrowsInterceptor | 例外の発生をトレースする |
| MockInterceptor | オブジェクトをモックとして振る舞わせる(テスト用) |
| DelegateInterceptor | ほかのクラスのメソッドに処理を委譲する |
| PrototypeDelegateInterceptor | S2コンテナからインスタンスを取得して、そのインスタンスのメソッドに処理を委譲する |
| SyncInterceptor | メソッド呼び出しを同期させる |
| InterceptorChain | 複数のインターセプタをネストさせて実行する |
インジェクション
- コンストラクタ・インジェクション
- コンストラクタの引数にDIする。
<components> <component name="..." class="..."> <arg>...</arg> </component> </components>
- セッター・インジェクション
- セッターメソッドにDIする。
<components> <component name="..." class="..."> <property name="プロパティ名">...</property> </component> </components>
- メソッド・インジェクション
- メソッドにDIする。
<components> <component name="..." class="..."> <initMethod name="メソッド名"> <arg>引数</arg> </initMethod> </component> </components>
- フィールド・インジェクション
- フィールドにDIする。
Bindingアノテーションを使って指定。2.4.17からはpublicなフィールドをプロパティとして認識するようになったので、@Binding("foo") private Foo foo;
publicフィールドを定義しておけば自動バインディングが 適用されます。
Creator(自動登録)
S2ではSMART deploy機能により特定のクラス名は自動的にコンポーネントとして登録される。
以下のクリエータをcreator.diconファイルで定義しておくと、対応したクラス名は自動で登録される。
| クリエータ | 対象クラス名 | インスタンス属性 |
| ActionCreator | Action | request |
| ConverterCreator | Converter | prototype |
| DaoCreator | Dao | prototype |
| DtoCreator | Dto | request |
| DxoCreator | Dxo | singleton |
| HelperCreator | Helper | prototype |
| InterceptorCreator | Interceptor | prototype |
| LogicCreator | Logic | prototype |
| PageCreator | Page | request |
| ServiceCreator | Service | prototype |
| ValidatorCreator | Validator | prototype |
インスタンス属性
| instance属性 | 説明 |
| singleton(default) | S2Container.getComponent()を何度呼び出しても同じインスタンスが返されます。 |
| prototype | S2Container.getComponent()を呼び出すたびに新たなインスタンスが返されます。 |
| request | リクエスト毎に1つのインスタンスが作成されます。 name属性に指定した名前で、コンポーネントがリクエストに格納されます。 requestを利用するためにはS2ContainerFilterの設定が必要です。 |
| session | セッション毎に1つのインスタンスが作成されます。 name属性に指定した名前で、コンポーネントがセッションに格納されます。 sessionを使う場合は、S2ContainerFilterの設定が必要です。 |
| application | Servletを使う場合は、ServletContext毎に1つのインスタンスが作成されます。 name属性に指定した名前で、コンポーネントがServletContextに格納されます。 applicationを利用するためにはS2ContainerFilterの設定が必要です。 |
| outer | コンポーネントのインスタンスは、S2Container外で作成し、Dependency Injectionだけを行います。 アスペクト、コンストラクタ・インジェクションは適用できません。 |
OGNL式
XMLの中で、文字列で記述した内容(式)をJavaのオブジェクトに変換するもの。
| 例 | 種別 | 説明 |
| "hoge" | 文字列 | "で囲みます。 |
| 'a' | char | 'で囲みます。 |
| 123 | 数値 | そのまま記述します。 |
| true,false | 論理値 | そのまま記述します。 |
| new java.util.Date(0) | コンストラクタ呼び出し | クラスの完全限定名 |
| @java.lang.Math@max(1, 2) | staticなメソッドを呼び出した結果の参照 | |
| @java.lang.String@class | クラス | |
| hoge.toString() | メソッドを呼び出した結果を参照。hogeは事前に定義したコンポーネント名。 | |
Seaser2でDB
http://www.atmarkit.co.jp/fjava/rensai4/saweb05/saweb05_3.html
接続するデータベースの設定(S2JTA)
org.seasar.extension.dbcp.impl.XADataSourceImplクラスのプロパティとして設定
(JTAのXAResourceをエミュレート)
jdbc.dicon
<component name="xaDataSource"
class="org.seasar.extension.dbcp.impl.XADataSourceImpl">
<property name="driverClassName">"com.mysql.jdbc.Driver"</property>
<property name="URL">"jdbc:mysql://localhost/accountdb"</property>
<property name="user">"root"</property>
<property name="password">"admin"</property>
</component>
コネクションプールの設定(S2DBCP)
<component name="connectionPool"
class="org.seasar.extension.dbcp.impl.ConnectionPoolImpl">
<property name="timeout">600</property>
<property name="maxPoolSize">10</property>
<property name="allowLocalTx">true</property>
<destroyMethod name="close" />
</component>
allowLocalTx……JTAの制御下にないJDBCのローカルトランザクションを許可する(true)
トランザクションの設定(S2Tx)
j2ee.diconをインクルードしておくと以下が使える。
| トランザクション属性 | インターセプタのクラス名 | j2ee.diconでの定義 | 説明 |
| Required | RequiredInterceptor | j2ee.requiredTx | トランザクションが開始されていれば利用。されていなければ開始される。 |
| RequiresNew | RequiresNewInterceptor | j2ee.requiresNewTx | 別途、新しくトランザクションを開始する。 |
| Mandatory | MandatoryInterceptor | j2ee.mandatoryTx | 既にあるトランザクションを利用。開始されていない場合エラー |
| NotSupported | NotSupportedInterceptor | j2ee.notSupportedTx | トランザクションを利用しない。既に開始されている場合そのトランザクションは利用されない。 |
Seasar2では規約に基づいてコンポーネントの自動登録される。
このとき、Pageクラスや Service クラスなど,コンポーネントの種類毎に様々な特性をカスタマイズすることができる。
特性のカスタマイズを行うのが Customizer と呼ばれるコンポーネントで,customizer.dicon という設定ファイルに定義する。
customizer.dicon
<component name="serviceCustomizer"
・・・
<initMethod name="addCustomizer">
<arg>
<component
class="org.seasar.framework.container.customizer.TxAttributeCustomizer"/>
</arg>
</initMethod>
として、TxAttributeCustomizerが
actionCustomizer serviceCustomizer logicCustomizer
に記述されている。
この設定により,XxxServiceでObject クラスで定義されたものを除いた全ての public なメソッドに,宣言的トランザクションが適用される。
TxAttributeCustomizerを注入した箇所がトランザクション境界となる。
デフォルトのトランザクション属性は Requiredとなる。
Actionクラスのメソッドが呼ばれた場合にトランザクションが開始され、
このメソッドが終了した時にトランザクションが終了する。
(Exceptionがthrowされて終了する場合はrollback、正常終了時はcommit)
Action、Service、logicと3つ定義されているが、この場合、
どれか最初に呼び出された部分でトランザクションが開始される。
(Actionの中からlogicが呼ばれる場合、logicでは何もされない)
O/Rマッピング(S2Dao)
- HibernateなどのO/RマップではXMLでDBテーブルとクラスの関連付けをするが、
S2Daoではstatic final型のクラス変数を用いてマッピングする。
(S2Daoではアノテーションと呼ぶがJava5のアノテーションとは別物)
単純なSELECTなどは定型のJavaメソッド名を定義するだけでSQLを書かなくて良い。メソッド名 引数 戻り値 INSERT文 insert、add、createで始める エンティティクラス voidかint UPDATE文 update、modify、storeで始める エンティティクラス voidかint DELETE文 delete、removeで始める エンティティクラス voidかint SELECT文 - - エンティティクラスもしくはその配列、あるいはListにする - バージョン番号による排他制御が簡単にできる
- S2DaoTestCaseあり。
- Daoインタフェースのアノテーション
BEAN 対応するエンティティクラス ARGS SQLコメントに記述する引数とメソッドの引数とを関連付ける QUERY 自動生成されるSELECT文の後に追加するWHERE句やORDER BY句 PERSISTENT_PROPS 永続化したい(SQLに含めたい)プロパティの名称 NO_PERSISTENT_PROPS 永続化したくない(SQLに含めたくない)プロパティの名称 SQL 実行したいSQL文を直接指定する場合
- エンティティクラスのアノテーション
TABLE 対応するテーブル COLUMN 変数名を"プロパティ名_COLUMN"とする。プロパティ名とカラム名が異なるときに、それらを関連付ける。 ID 変数名を"プロパティ名_ID"とする。プライマリキーのIDを設定するプロパティ名 NO_PERSISTENT_PROPS 永続化したくない(SQLに含めたくない)プロパティ名 VERSION_NO_PROPERTY バージョン番号による排他制御を行うときに用いるプロパティ名 TIMESTAMP_PROPERTY Timestampによる排他制御を行うときに用いるプロパティ名
- SQLファイル
- S2DaoではSQLファイルは”インターフェイス名またはクラス名_メソッド名.sql”にし、クラスと同階層に配置する。
- SQLコメント
- ※ /*のあとにスペースをつけるとただのコメントになるので注意。
バインド変数コメント /*変数名.プロパティ名*/ 埋め込み変数コメント /*$変数名.プロパティ*/ IFコメント /*IF 条件式*/リテラル/*END*/ BEGINコメント /*BEGIN*/WHERE句/*END*/