Ymirではリクエストパスに対応するPageクラスのメソッドが呼び出されます。対応するPageクラスが存在しない場合、メソッド呼び出しは行なわれません。このクラスが存在する場合、以下のような処理が行なわれます。
- このクラスのインスタンスをS2Containerから取り出す(なおインスタンスはrequestスコープです)。なお、Pageインスタンスについては、S2Containerによる暗黙的なDIは行なわれません。 S2ContainerによるDIを行ないたい場合は、@Bindingアノテーションを使って明示的に指定するか、Ymirの@Injectアノテーションを使用して下さい。
- @Inアノテーションが付与されているsetterメソッドがあれば、アノテーションに従ったオブジェクトスコープからオブジェクトを取り出してそのsetterメソッドを使ってセットする。
- @Populateアノテーションが付与されているGetter/Setterメソッドがあれば、アノテーションに従ったオブジェクトスコープからオブジェクトを取り出してそのGetter/Setterメソッドを使って値をセットする(リクエストパラメータはここでセットされます)。
- Pageクラスに書かれている制約アノテーションに従って制約(リクエストパラメータの正当性やアクセス権限)のチェックを行なう。
- Pageインスタンスについてアクションを実行する。具体的には、マッチしたパスマッピングから構築したアクション名と同じ名前のメソッドを呼び出す。アクション名と同じ名前のメソッドが存在しない場合は_default()メソッドを呼び出す。_default()メソッドも存在しない場合はActionNotFoundRuntimeExceptionをスローする。
- メソッドの返り値からResponseオブジェクトを構築する。構築したResponseのtypeがPASSTHROUGHである場合、リクエストパスに対応するテンプレートファイルが存在しないかつマッチしたパスマッピングから構築したデフォルトの返り値がnullでない場合は、Responseオブジェクトをデフォルトの返り値から構築したResponseオブジェクトで置き換える。
- ResponseのtypeがPASSTHROUGHの場合、Pageインスタンスの_prerender()メソッドを呼び出す。(このメソッドの中で、テンプレートのレンダリングに必要なオブジェクトやプロパティの準備を行なうようにして下さい。)
- @Outアノテーションが付与されているGetterメソッドがあれば、アノテーションに従ったオブジェクトスコープにGetterメソッドの返り値を格納する。
なお、制約チェックで失敗した場合はアクションメソッドの呼び出しの代わりに、_validationFailedメソッドまたは_permissionDeniedメソッドが呼び出されます。具体的には、リクエストパラメータの正当性チェックに失敗した場合は_validationFailedメソッドが呼び出され、アクセス権限チェックに失敗した場合は_permissionDeniedメソッドが呼び出されます。
_validationFailedメソッドまたは_permissionDeniedメソッドの呼び出しの後は、通常のアクションメソッドの場合と同じように返り値からResponseオブジェクトが構築されます。また_prerender()メソッドの呼び出し処理も通常と同じように行なわれます。
_validationFailedメソッドが存在しない場合は単にメソッド呼び出しがスキップされますが、_permissionDeniedメソッドが存在しない場合はPermissionDeniedExceptionがスローされます。_validationFailedメソッドが存在しない場合は_prerender()メソッドが呼び出されますが、_permissionDeniedメソッドが存在せずにPermissionDeniedExceptionがスローされた場合は_prerender()メソッドの呼び出しは行なわれません。
アクションメソッドの引数値の解決
アクションメソッドが引数を持つ場合、引数に@org.seasar.ymir.scope.annotation.Resolveアノテーションを付与することでスコープからオブジェクトを取り出して引数の値とすることができます。これを「メソッド引数値の解決(resolution)」と呼びます。
引数にアノテーションが付与されていない場合はアクションメソッド毎に定義されているデフォルトの引数値が第1引数から順番に割り当てられます。それでも解決できない引数には引数の型に応じたデフォルト値が割り当てられます。
デフォルトの引数値は以下のように定義されています。
アクションメソッド | デフォルトの引数値 |
---|---|
(ボタンに対応しないアクション) | なし |
(ボタンに対応するアクション) | ボタン名にname[param]のようにパラメータが付与されていればその値 |
_prerender | なし |
_validationFailed | エラーメッセージ情報を持つNotesオブジェクト |
_permissionDenied | スローされるためのPermissionDeniedExceptionオブジェクト |
例
標準のマッピングでは、例えばリクエストパスが/path.htmlである場合、アプリケーションのルートパッケージ名をcom.example.appとしてcom.example.app.web.PathPageクラスがPageクラスとなります。
今HTTPメソッドがPOSTであるようなリクエストが/path.htmlに対してなされた場合の処理の流れは以下のようになります。
制約チェックでエラーにならないケース
- PathPageインスタンスの取り出し
- スコープ内オブジェクトのinject
- PathPage#_post()、なければPathPage#_default()の呼び出し
- PathPage#_prerender()の呼び出し
リクエストパラメータの正当性チェックでエラーになるケース
- PathPageインスタンスの取り出し
- スコープ内オブジェクトのinject
- PathPage#_validationFailed()の呼び出し
- PathPage#_prerender()の呼び出し
アクセス権限チェックでエラーになるケース
- PathPageインスタンスの取り出し
- スコープ内オブジェクトのinject
- PathPage#_permissionDenied()の呼び出し、なければPermissionDeniedExceptionをスロー
- (PermissionDeniedExceptionがスローされなかった場合)PathPage#_prerender()の呼び出し
リクエストパラメータのインジェクト
リクエストパラメータはユーザが自由に外部から指定することができるため、リクエストパラメータをGetter/Setterメソッドを使って自動的にPageオブジェクトに設定できるようにしてしまうとアプリケーション設計者の意図しないGetter/Setterメソッドを使って外部からPageオブジェクトに値がセットされてしまう危険性があります。
この危険を回避するために、Ymirではリクエストパラメータスコープから値をインジェクトまたはポピュレートするようなアノテーションをGetter/Setterメソッドに明示的に付与するポリシーになっています。このアノテーションが付与されていないGetter/Setterはリクエストパラメータは代入されません。
ある名前のリクエストパラメータを代入したい場合は例えば以下のように記述して下さい:
@In(scopeClass = RequestParameterScope.class, name = "parameter") public void setParameter(String parameter) { this.parameter = parameter; }
Setterに対応するプロパティ名と代入したいパラメータの名前が同じ場合はもう少し簡単に記述できます:
@In(RequestParameterScope.class) public void setParameter(String parameter) { this.parameter = parameter; }
さらにこれはエイリアスアノテーションを使って以下のように書けます:
@RequestParameter public void setParameter(String parameter) { this.parameter = parameter; }
ネストした形式のパラメータをオブジェクトグラフにマッピングするような形で代入したい場合は@Inアノテーションの代わりに@Populateアノテーションを使います。例えばuser.nameというパラメータの値をPage#getUser().setName()を使って代入したい場合は以下のように記述して下さい:
@Populate(RequestParameterScope.class) public User getUser() { return user; }
実は@RequestParameterは@Populate(RequestParameterScope.class)のエイリアスです。そのためSetterに@RequestParameterと@Inの両方のアノテーションが付与されている場合、@RequestParameterの評価順序は@Inよりも後になります。
フェーズ毎のカスタムメソッド呼び出し
Pageコンポーネントに関する処理フェーズ毎に特定のメソッドを呼ぶようにすることができます。
ある処理フェーズの時点で呼び出したいメソッドにはorg.seasar.ymir.annotation.Invokeアノテーションを付与します。アノテーションのプロパティとして、フェーズを表すEnum(org.seasar.ymir.Phase)を指定します。以下に例を示します:
@Invoke(Phase.PAGECOMPONENT_CREATED) public void initialize() { ... }
フェーズには以下のものがあります。
フェーズ名 | 説明 |
---|---|
PAGECOMPONENT_CREATED | Pageコンポーネントの生成直後(1.の直後) |
OBJECT_INJECTED | オブジェクトのインジェクトの直後(2.の直後) |
ACTION_INVOKING | アクションの実行前(6.の直前) |
ACTION_INVOKED | アクションの実行後(6.の直後) |
Invokeアノテーションが付与されたメソッドが引数を持つ場合はアクションメソッドと同様に引数値の解決が行なわれます。デフォルトの引数値はありません。
[Ymir-1.0.2以降] Invokeアノテーションが付与されたメソッドが返り値を返した場合、その返り値に従って画面遷移が行なわれます。