いきなりコアな話ですが、struts2で何気に大事なのがValueStackです。

何回になるかは分かりませんがこのValueStackの話について記述していきたいと思います。

ValueStack自体はインターフェースなので実際に使われているのはOgnlValueStackというクラスになります。

このValueStackの動きを把握することで、struts2内部でのデータのやり取りが分かる他、画面に値が表示されないときなどの問題解決に非常に役に立ちます。

struts2では、このOgnlValueStackというクラスに、アクションクラスやモデル(ModelDrivenのモデル)をpushし、setValueというメソッドでスタックにpushした値をセットし、findValueというメソッドでスタックにpushしたオブジェクトから値を取得しているようです。

OgnlValueStackの動作

それではこのValueStackの動作を確認していきます。

まずはサンプルとなるアクションクラスです。

public class TestAction extends ActionSupport{
  private String name;
  private String address;
	
  public String getName() {return name;}
  public void setName(String name) {this.name = name;}
  public String getAddress() {return address;}
  public void setAddress(String address) {this.address = address;	}
}

次にモデルクラスです。

public class TestModel {
  private String id;
  private String name;
  public String getId() {	return id;}
  public void setId(String id) {this.id = id;}
  public String getName() {return name;}
  public void setName(String name) {this.name = name;}
}


以下がテストケースとなります。

public class TestOgnlValueStack  extends TestCase {

	public void testアクションクラス() {

		OgnlValueStack valueStack = new OgnlValueStack();

		TestAction testAction = new TestAction();
		testAction.setName("action_name");
		testAction.setAddress("action_address");

		valueStack.push(testAction);

		assertEquals("action_name", valueStack.findValue("name"));

		assertEquals("action_address", valueStack.findValue("address"));
		assertNull("見つからない場合はnull", valueStack.findValue("id"));

		// setValueでActionクラスのsetNameを呼ぶ
		valueStack.setValue("name", "nameを書き換えました");

		assertEquals("nameを書き換えました", valueStack.findValue("name"));

	}

	public void testアクションクラスとモデル() {

		// アクションクラスの上にモデルをpushします。
		//
		// 1. モデル
		// 2. アクションクラス
		// という構造でStackにつまれている。
		//

		OgnlValueStack valueStack = new OgnlValueStack();

		TestAction testAction = new TestAction();
		testAction.setName("action_name");
		testAction.setAddress("action_address");

		TestModel testModel = new TestModel();
		testModel.setName("model_name");
		testModel.setId("model_id");

		valueStack.push(testAction);
		valueStack.push(testModel);

		assertEquals("model_name", valueStack.findValue("name"));
		assertEquals("action_address", valueStack.findValue("address"));
		assertEquals("model_id", valueStack.findValue("id"));

		valueStack.setValue("name", "nameを書き換えました");

	}

	public void test1000件下でも取れるよ() {

		OgnlValueStack valueStack = new OgnlValueStack();
		// 1番最初にモデルをpush
		TestModel testModel = new TestModel();
		testModel.setId("1000件下でもモデルのIDが取れます");

		valueStack.push(testModel);

		// 1000オブジェクトをpush
		for (int i = 0; i < 1000; i++) {

			TestAction testAction = new TestAction();
			testAction.setName("アクション" + i + "番目");
			valueStack.push(testAction);
		}

		assertEquals("1000件下でもモデルのIDが取れます", valueStack.findValue("id"));
		assertEquals("最後にpushされたアクションクラスのgetNameがよばれる", "アクション999番目",
				valueStack.findValue("name"));

		valueStack.setValue("id", "1000件下でも書き換えられます");
		assertEquals("1000件下でも書き換えられます", valueStack.findValue("id"));

	}

}

上のテストケースから言えることは、

  • findValue()はスタックに詰まれているオブジェクトから、引数に合致するgetterをよび値を取得する。
  • setValue()はスタックに詰まれているオブジェクトから、引数に合致するsetterをよび値をセットする。
  • findValue()、setValue()とも、スタックの上から検索される。
  • findValue()、setValue()とも、一度setter、getterが見つかると、それ以上の処理は行わない

上記の仕様を踏まえて、簡単ですがアクションクラスに値が登録するまでの説明をします。
※まちがってたらごめんなさい

ModelDrivenを使っていない場合

※細かいの部分は省いています。

  1. ブラウザからリクエス
  2. アクションクラスがValueStackにpsuhされる。
  3. ParametersInterceptor::setParameters()でsetValue("inputタグの名称","inputタグの値")がセットされる。
  4. アクションクラスのメソッドが起動
  5. 結果クラスがValueStackから必要なデータをfindValue()する。
  6. アクションクラスがValueStackにpopされる。

ModelDrivenを使う場合

※ModelDrivenを使う場合、アクションクラスに implements ModelDrivenをインプリメントし、getModel()を実装する。
※また、ModelDrivenInterceptorを使うようにstruts.xmlを記述する。

  1. ブラウザからリクエス
  2. アクションクラスがValueStackにpsuhされる。
  3. ModelDrivenInterceptorから、ModelDriven::getModel()がよばれ、ValueStackにpushされる。
  4. ParametersInterceptor::setParameters()でsetValue("inputタグの名称","inputタグの値")がセットされる。※testアクションクラスとモデル()と同じ状態
  5. アクションクラスのメソッドが起動
  6. 結果クラスがValueStackから必要なデータをfindValue()する。
  7. アクションクラスがValueStackにpopされる。


と、内部ではこのようなデータのやりとりがされています。

とりあえず、今日はここまで