yukungのブログ

yukungの技術ブログ兼駄文置き場 

Seasar2再学習めも

久しぶりに時間が取れたので,

Seasar 2 徹底入門 SAStruts/S2JDBC 対応

Seasar 2 徹底入門 SAStruts/S2JDBC 対応

を読んでSeasar2を再学習したのでそのメモ。

S2Containerの初期化

  • SingletonS2ContainerFactory#init()
  • app.diconの内容でコンテナを初期化する

コンポーネントの取得

  • S2Container#getComponent()で取得
  • できるだけ型を指定して取得するほうが吉

DIの設定

  • 自動バインディングをうまく利用することでdiconファイルの記述量を減らすことができる
  • インタフェースを実装していないコンポーネントをDIする場合や,同じインタフェースを実装した複数のコンポーネントが登録されている場合は,diconファイルで明示的にDIの設定を記述する。

ライフサイクルの選び方

singleton,application,sessionなどライフサイクルが長い場合はスレッドセーフであることを十分に留意する必要がある。request,session,applicationはWebアプリケーション環境下のみで指定可能。利用する場合はweb.xmlにてS2ContainerFilterの設定が必要。

初期化と破棄のタイミング

DIできないライフサイクル

outerコンポーネントの使い方

インジェクションの種類

  • コンストラクタインジェクション
  • セッターインジェクション/フィールドインジェクション
    • componentタグの配下にpropertyタグを記述する
  • メソッドインジェクション
    • componentタグの配下にinitMethodタグを用いて呼び出すメソッドを指定する

自動バインディング

autoBinding属性 内容
auto コンストラクタとプロパティに対して自動バインディングを行う。autoBinding属性を省略した場合はauto
semiauto アノテーションなどで明示的に指定したプロパティに対して自動バインディングを行う
constructor コンストラクタに対して自動バインディングを行う
property プロパティに対して自動バインディングを行う
none 自動バインディングを行わない
  • 自動バインディングが有効でも,diconファイルで明示的にインジェクションの設定が行われている場合は,そちらが有効

コンストラクタへの自動バインディング

プロパティへの自動バインディング

  • プロパティの型に一致する型のコンポーネントが登録されており,なおかつコンポーネント名とプロパティ名が一致するコンポーネントがあればセットされる
  • プロパティの型がインタフェースの場合,インタフェースを実装したコンポーネントがあればセットされる
  • プロパティの型がインタフェースの配列の場合,コンテナ内でインタフェースを実装したコンポーネントの配列がセットされる

外部バインディング

Webアプリケーションにおいて,リクエストパラメータの値などを自動的にコンポーネントにセットするために使用される。componentタグのexternalBinding属性をtrueに設定することで利用できる。コンポーネントにはリクエストパラメータ名と同じ名前でプロパティを作成しておくと,S2Containerによって自動的にリクエストパラメータが設定される。

Seasar2が提供するインターセプター

Seasar2にはデフォルトでいくつかのインターセプターが用意されている。これらのインターセプターの中にはaop.diconであらかじめ定義されているものもあり,aop.diconをインクルードしていればaspectタグでインターセプターのコンポーネント名を指定するだけで利用することができる。
SMART deploy対象のコンポーネントの場合,アノテーションによってインターセプターを適用することも可能。@Traceアノテーションをメソッドに記述すると,diconファイルで設定を行わなくてもTraceInterceptorが適用される。

クラス名 コンポーネント 説明 アノテーション定義
TraceInterceptor aop.traceInterceptor トレースログを出力する @Trace
ClassLoaderAwareTraceInterceptor aop.classLoaderAwareTraceInterceptor クラスローダの情報も出力するTraceInterceptor -
SimpleTraceInterceptor aop.simpleTraceInterceptor シンプルなトレースログを出力する @SimpleTrace
SyncInterceptor aop.syncInterceptor メソッドを同期化する @Sync
TraceThrowsInterceptor aop.traceThrowsInterceptor 例外のトレースログを出力する @TraceThrows
ToStringInterceptor aop.toStringInterceptor フィールドの情報をtoString()で出力する @ToString
RemoveSessionInterceptor aop.removeSessionInterceptor メソッド終了時にセッションに格納されているオブジェクトを削除する @RemoveSession
InvalidateSessionInterceptor aop.invalidateSessionInterceptor メソッド終了時にセッションを破棄する @InvalidateSession
DependencyLookupInterceptor aop.dependencyLookupInterceptor S2Containerから取得したコンポーネントを返却する @DependencyLookup
InterceptorLifecycleAdapter aop.lifecycleAdapter ライフサイクルがsingleton以外のインターセプターを使うためのアダプタ*1 -
DelegateInterceptor diconファイルには未登録 メソッド呼び出しを他のメソッドに委譲する*2 -

インターセプターのグループ化

InterceptorChainを使って複数のインターセプターをグループ化できる。

独自インターセプターの作成

  • org.aopalliance.intercept.MethodInterceptorインタフェースを継承
    • AOPアライアンス標準のインタフェース
  • org.seasar.framework.aop.interceptors.AbstractInterceptorを継承
    • Seasar2に特化したいくつかのメソッドが実装されている

上記2つの方法で実装する。

独自のインターセプターをアノテーションで適用できるようにする

上記のルールでインターセプターを適用するためのアノテーションが利用できる。

インタータイプ

インタータイプによって,クラスの静的な構造を変更(クラスにフィールドやメソッドを追加)することができる。

PropertyInterType

フィールドに対するsetter/getterメソッドを追加するインタータイプ。
Seasar2はpublicフィールドに対応しているため,単純に値を格納するだけのプロパティであればアクセサメソッドを作る必要はないが,アクセサメソッドがないとプロパティとして扱ってくれない外部のライブラリと組み合わせて使用する場合に便利。
privateなフィールドに対してはアクセサメソッドを生成しないので注意。
@Propertyアノテーションでアクセサメソッドの生成を制御できる。

InterTypeChain

複数のインタータイプをグループ化し,再利用しやすくする。

独自インタータイプの作成

org.seasar.framework.aop.InterTypeインタフェースを継承して実装する。

AOPによるトランザクション制御

Seasar2ではトランザクションの制御を行うためのインターセプターが用意されており,j2ee.diconで定義されている。

クラス名 コンポーネント 説明
RequiredInterceptor j2ee.requiredTx トランザクションが開始されていなければ,自動的にトランザクションを開始する。すでにトランザクションが開始されていれば,そのトランザクションを引き継ぐ。
RequiresNewInterceptor j2ee.requiresNewTx 常に新しいトランザクションを開始させる。既存のトランザクションが開始されているなら,既存のトランザクションを中断し,自分自身のトランザクションの終了後,中断したトランザクションを復帰させる
MandatoryInterceptor j2ee.mandatoryTx トランザクションがすでに開始されていなければエラーにする
NotSupportedInterceptor j2ee.notSupportedTx 既存のトランザクションが開始されているなら,既存のトランザクションを中断する。コンポーネントのメソッドの終了後,中断したトランザクションを復帰させる
NeverInterceptor j2ee.neverTx トランザクション制御を行わない。トランザクションが開始されていた場合はエラーにする

インターセプターが適用されたメソッド内で例外が発生した場合,基本的にトランザクションロールバックされる。ただし,例外はスローしたいがトランザクションはコミットしたいケースは,インターセプターにaddCommitRule()メソッドで例外の型を登録しておくと,特定の例外が発生した場合にコミットすることができる。
Seasar2のSMART deploy機能を使用する場合はこれらのインターセプターを個別に適用するのではなく,カスタマイザという仕組みを使ってアクションクラスに一括してトランザクション制御のためのインターセプターを適用する。*3

AopProxy

org.seasar.framework.aop.proxy.AopProxyを使うと,S2Containerを使わずにJavaコードで任意のオブジェクトに対してAOPを適用することができる。

コンポーネントの自動登録

S2Containerには特定の命名規則に従って作成されたクラスを自動的にコンポーネントとして登録するためのComponentAutoRegisterというクラスが用意されており,diconファイルに登録しておくことで,コンポーネントの自動登録を行うことができる。
ComponentAutoRegisterにはいくつかの種類がある。

アスペクトの自動登録

AspectAutoRegisterを使用すると,アスペクトの登録も自動化できる。アスペクトの自動登録をコンポーネントの自動登録と組み合わせて使用する場合,ComponentAutoRegisterの定義よりも後にAspectAutoRegisterの定義を記述する必要がある点に注意。
AspectAutoRegisterの実装には,以下の2種類がある。

  • AspectAutoRegister
    • クラス名のパターンを指定してアスペクトを自動登録
  • InterfaceAspectAutoRegister
    • 指定したインタフェースを実装したクラスに対してアスペクトを自動登録

アノテーション

Seasar2では,DIの設定は基本的にdiconファイルで行うが,アノテーションを使ってDIやAOPの設定を行うことができる。

アノテーション 説明
@Component S2Containerで管理するクラスに付与する。diconファイルのcomponentタグに相当
@Binding DIするプロパティに付与する。diconファイルのpropertyタグに相当
@Aspect AOPを適用するクラスまたはメソッドに付与する。diconファイルのaspectタグに相当
@InterType InterTypeに適用するクラスに付与する。diconファイルのInterTypeタグに相当
@initMethod コンポーネントの初期化用メソッドに付与する。diconファイルのinitMethodタグに相当
@DestroyMethod コンポーネントの廃棄用メソッドに付与する。diconファイルのdestroyMethodタグに相当

@Bindingアノテーションと同等の機能を提供するアノテーションとして,@Resourceアノテーションも存在する。@ResourceアノテーションJavaEE標準のアノテーションであるため,通常は@Resourceアノテーションを利用することが推奨されている。ただし,@ResourceアノテーションはDIするコンポーネントが見つからない場合にエラーとなってしまう。コンポーネントが存在する場合のみDIするには,@Resourceアノテーションの代わりに,bindingType属性にBindingType.MAYを指定した@Bindingアノテーションを使用するとよい。

Webアプリケーションでの利用

S2ContainerをWebアプリケーションで利用する場合,web.xmlでの設定が必要。

  • S2ContainerServletの登録
    • Webアプリケーションのコンテキスト起動時にS2Containerの初期化を行う。
  • S2ContainerFilterの登録
    • コンポーネントのライフサイクル(application,session,request)を使用するために登録。
  • HotdeployFilterの登録
    • S2ContainerFilterよりも前に適用されるように設定する必要がある。
      • S2ContainerFilterよりも上にHotdeployFilterを記述する。
  • 文字化け対策
    • EncodingFilterをweb.xmlに登録
      • 全てのフィルタよりも先に適用されるように設定。
    • Tomcatの場合
      • server.xmlのConnectorタグにuseBodyEncodingForURI属性を追加して,trueを設定

暗黙コンポーネント

以下のコンポーネントについては,diconファイルで特に設定を行わなくても利用することができる。

コンポーネント 備考
S2Container container コンテナ
HttpServletRequest request リクエスト
HttpServletResponse response レスポンス
ServletContext application サーブレットコンテキスト
Map applicationScope ServletContextの属性
Map initParam ServletContextの初期化パラメータ
Map sessionScope HttpSessionの属性
Map requestScope HttpServletRequestの属性
Map cookie クッキー
Map header リクエストヘッダ(値はString)
Map headerValues リクエストヘッダ(値はString[])
Map params HttpServletRequestのパラメータ(値はString)
Map paramValues HttpServletRequestのパラメータ(値はString[])

以下のようにフィールドを定義しておくと,S2ContainerによってHttpServletRequestやHttpServletResponseがDIされる。

public class HelloAction {
	@Resource
	protected HttpServletRequest request;
	@Resource
	protected HttpServletResponse response;
}

環境の切替

env.txtによって環境を定義している。

環境名 説明
ut 単体テスト環境
ct 結合テスト環境
it 統合テスト環境
product 運用環境。env.txtが存在しない場合はこの値がデフォルト値

diconファイルでは#ENVという変数で環境名を参照可能。

*1:インターセプターはコンポーネントの登録時にS2Containerから取得されるため,インターセプターのライフサイクルをprototypeやrequestに設定していても,コンテナの初期家事にインスタンス化されてしまう

*2:インタフェースのメソッドもしくはabstractメソッドの呼び出しを他のオブジェクトに転送する

*3:Doltengでプロジェクトを作成すると,標準でその設定になっている