Java/Seasar2

Last-modified: 2013-03-11 (月) 15:30:31

リンク

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ほかのクラスのメソッドに処理を委譲する
PrototypeDelegateInterceptorS2コンテナからインスタンスを取得して、そのインスタンスのメソッドに処理を委譲する
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アノテーションを使って指定。
@Binding("foo")
private Foo foo;
2.4.17からはpublicなフィールドをプロパティとして認識するようになったので、
publicフィールドを定義しておけば自動バインディングが 適用されます。

Creator(自動登録)

S2ではSMART deploy機能により特定のクラス名は自動的にコンポーネントとして登録される。
以下のクリエータをcreator.diconファイルで定義しておくと、対応したクラス名は自動で登録される。

クリエータ対象クラス名インスタンス属性
ActionCreatorActionrequest
ConverterCreatorConverterprototype
DaoCreatorDaoprototype
DtoCreatorDtorequest
DxoCreatorDxosingleton
HelperCreatorHelperprototype
InterceptorCreatorInterceptorprototype
LogicCreatorLogicprototype
PageCreatorPagerequest
ServiceCreatorServiceprototype
ValidatorCreatorValidatorprototype

インスタンス属性

instance属性説明
singleton(default)S2Container.getComponent()を何度呼び出しても同じインスタンスが返されます。
prototypeS2Container.getComponent()を呼び出すたびに新たなインスタンスが返されます。
requestリクエスト毎に1つのインスタンスが作成されます。
name属性に指定した名前で、コンポーネントがリクエストに格納されます。
requestを利用するためにはS2ContainerFilterの設定が必要です。
sessionセッション毎に1つのインスタンスが作成されます。
name属性に指定した名前で、コンポーネントがセッションに格納されます。
sessionを使う場合は、S2ContainerFilterの設定が必要です。
applicationServletを使う場合は、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での定義説明
RequiredRequiredInterceptorj2ee.requiredTxトランザクションが開始されていれば利用。されていなければ開始される。
RequiresNewRequiresNewInterceptorj2ee.requiresNewTx別途、新しくトランザクションを開始する。
MandatoryMandatoryInterceptorj2ee.mandatoryTx既にあるトランザクションを利用。開始されていない場合エラー
NotSupportedNotSupportedInterceptorj2ee.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)

  1. 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にする
  2. バージョン番号による排他制御が簡単にできる
  3. S2DaoTestCaseあり。

Daoインタフェースのアノテーション
BEAN対応するエンティティクラス
ARGSSQLコメントに記述する引数とメソッドの引数とを関連付ける
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_PROPERTYTimestampによる排他制御を行うときに用いるプロパティ名
SQLファイル
S2DaoではSQLファイルは”インターフェイス名またはクラス名_メソッド名.sql”にし、クラスと同階層に配置する。
SQLコメント
バインド変数コメント/*変数名.プロパティ名*/
埋め込み変数コメント/*$変数名.プロパティ*/
IFコメント/*IF 条件式*/リテラル/*END*/
BEGINコメント/*BEGIN*/WHERE句/*END*/
※ /*のあとにスペースをつけるとただのコメントになるので注意。