Struts2のValueStackのイメージを図解で理解する【初心者向け】

- Java/Struts2 -
2019.11.09
struts2

こんにちは、ユーキです。

プログラマカレッジ2ヶ月目の前半にやった「Struts2」のログイン認証機能作成の課題。

Struts2ログイン認証

この課題を進めていて、疑問に思ったことがあります。

それは、ざっくり言うとファイルAで入力した値(名前とかパスワードとか)をファイルBはどうやって受け取っているのか!?

という問題。

抱いた具体的な疑問

  1. "welcome.jsp"で入力したユーザ名・パスワードを"LoginAction.java"ファイルはどうやって受け取っているのか?(受け取らないとDAOに引き渡してDB検索できない)
  2. "LoginAction.java"に「getter/setter」メソッドがあるのはなぜか?書いたはいいけどgetterもsetterも使ってないよ?
  3. ログイン成功画面"login.jsp"に<s:property name="username">と書くがなぜこれを書くだけで入力フォームにユーザが入力した値が表示されるのか?どこからも渡されてないし、受け取るコードも書いてないぞ?

プログラマカレッジの課題で言うと「フレームワーク演習1で作成するWebproj2のWelcome機能」に該当します。

万が一カレッジ生が読むことがあれば、何が書いてあるか分からないと思うので写経後に読むことを推奨。

※そもそもStruts2がよく分からん、というあなたはまず先に以下記事からどうぞ

【Struts2とは?】初歩の初歩をイメージでつかむ【第1章】

疑問①②③を図解するとこんな感じ↓。※細かいコードはところどころ省略してます

この「どっから値を引っ張り出してきてんの?」という疑問はJSP/サーブレットで、

String username = request.getParameter("username")

とか

String username = (String)session.getAttribute("username")

といった具合でファイル間での値の受け渡しを「リクエストスコープ/セッションスコープ」の概念でコードを書いたことのある人ほど不思議に感じるはず。

Struts2を使うと「このデータを受け取ります」と明示的にどこにも書いてないのにページAからページBへの値の受け渡しができてるんですよね。

これら3つの疑問は、Struts2のValueStack(バリュースタック)という概念が分かるとスッキリするということが分かりました。

「渡します」「受け取ります」と書いてないのに渡せるし受け取れる仕組みの犯人は、ValueStack。

とにかく「なんか前のページで入力した文字が次のページで表示されてんな」って時は、あなたの知らない間にValueStackが動いてくれているのです。

このValueStackがイメージできるようになることで、書いてるけど意味は分かってない写経から1ランクアップすることができます。

どうでもいいけど、ValueStackって言葉がカッコいいよね!

まず最初に"イメージ"できるようになろう

具体的なコードの内容を図解する前に、ValueStackはイメージトレーニングが大事だと思っています。

文字列だけでは曖昧で捉えづらいので、ある程度明確な「映像」として頭に焼き付けることができれば今後の学習が楽になるかと。

ValueStackは「箱」

まず、ValueStackは箱です

箱なので、中に色々な物をしまったり必要な時に取り出したりできるのです。

頭の中で、次の映像を想像してみてください。

ValueStackという名前のついたダンボール箱がカパッと口を開けて待っています。中に物をしまい込む気満々です。

ValueStack

大げさであればあるほど頭に残りやすくなります。想像できましたか?

箱単体のイメージができたら、次はもう少し具体的なストーリーでイメージトレーニングをします。

ValueStackは「自分勝手」。でもホントはイイやつ

ValueStackは変わった性格を持った箱です。

以降、頭にインパクトを残すためにあえて大げさなイラストで図解してみます。

A地点で送信ボタンが押された結果、とあるデータがB地点へと向けて旅立ちました。しかし、B地点には道は通じておらず受け取れません

この時、データの行く道の直下ではValueStackという箱が口を開けて待ち構えている絵を思い浮かべてください↓

ValueStackとは

※「B地点に道は通じておらず」というのは「B地点で受け取ります、というコードを明示的に書いていない」という意味です

ここから、さらにイメージを大げさにします。頭に焼き付けるためです。

B地点に向かうデータに向かって、ValueStackの箱から突然ズバーーン!と手が伸びてきて、送信されてくるデータを奪い取ります↓

そして、そっと箱の中へ仕舞い込みます。送信されたデータが奪われてしまいました。まるで強奪したかのよう。なんて自分勝手なヤツなんだ!↓

このように、入力フォームから送信された内容は目的地へ届く前にValueStackという箱の中にすっぽりと格納されてしまいます

・・・と思いきや、本来なら届けられないB地点へそっとデータを届けてくれました。なんてイイヤツなんだ。↓

僕は途中「自分勝手」と赤字で強調したのには理由があって、

僕もあなたも、誰も「データが近くを歩いてきたら横取りしてValueStackに格納しておきなさい」とか「格納したデータを目的地へ渡しなさい」とか一言もコードで書いた覚えがないのに勝手にそう動くようになっているからです。

※「書いてないのに動く」のがフレームワークの「スゴイところ」でもあり「怖いところ」でもあります。書かれていない=目に見えなければ理解も難しくなりますし、「ちゃんと動いていると思ったら実は余計で不要な動きまでしていて不具合へ繋がった(しかもどう動いているか見えないのでトラブルシュートも難しくなる)」というデメリットも考えられますね。

ValueStackの近くをデータがうろつくと、指示もされてないのに勝手に仕舞い込み親切にも目的のファイルへ引き渡してくれる。

こんな風に、データの受け渡しを自動的にやってくれるのがStruts2の重要な機能の一つ・ValueStackの正体でした。

自動的=便利=フレームワークすごい、というわけです。

ここまでイメージができていれば、あとは今までイラスト内で書いてきた「A地点」をフォーム入力内容を送信するJSPファイルに置き換え、「B地点」をValueStackから値を受け取るActionクラスファイルに置き換えてちょっと具体的にコードを読んでみるだけです。

ようやく本記事の折り返し地点です。もう少し、お付き合いくださいm(_ _)m

ログイン認証時のValueStackの働きを図解

そもそもこの記事の始まりは「ログイン認証フォームの課題をやっているときに疑問を感じた」というお話でした。ここまでが長くて忘れてしまった方のために、記事冒頭の絵を再掲します。

Struts2ログイン認証

このように、ブラウザからユーザ名とパスワードを送信するとファイル間をどんな流れでデータの受け渡しが発生するのかを図解していきます。

以下、送信ボタンをポチった直後のイメージです。

「ズバーン!」とValueStackから手が伸びてきて送信されたデータを格納する、というのをマイルドかつ現実味を帯びたイラストで表現するとこんな感じ。

JSPファイルから送信ボタンを押すと、Struts2の機能であるValueStackという箱に入力された値が勝手に保存されます。

このあたりのイメージは大事なことなのでしつこくなんども繰り返します。

図のように「送信」をポチった瞬間にValueStackという別箱が勝手に出来上がり、その中に値が保存される映像をイメージしてみてください

勝手に、と強調したのは「ValueStackにusername変数、password変数を用意して中身を保存しておけ」なんてコードを自分で書いていないからでしたね。

Struts2が勝手に裏側でやってくれることです。

ポイント

JSPから入力され送信された値は、ValueStackという箱へ一旦吸い込まれるイメージを持つこと

さて、次はActionクラスファイル(今回はLoginAction.java)がJSPファイルから「名前」と「パスワード」を受け取ってログイン処理を行う部分です。

本来なら「JSPから送られてきたデータをActionで受け取るよ」というコードを書いてないので受け取れません。

書いてないにも関わらず、Struts2のValueStackが勝手に受け渡しをやってくれるのでしたね。

具体的には、Struts2がActionファイルのsetterメソッドを使ってValueStackに保存されていた値をメンバ変数にセットします↓

ValueStackを分かりやすく

JSPからの入力値をActionファイルで受け取るためにはActionファイルにsetterメソッドを書いておく必要があるという条件を忘れないようにしましょう

って受け取るコードは書くんかーーい!!受け取るコードは書いてない言うとったやろ!?ってツッコンだ方は正しいです(^^; でも「setterでJSPからの値を受け取る」なんて知らずに書いていませんでしたか...?

後で詳しく書いていますが、setterを書き忘れるとValueStackから値を取ってこれずプログラムが正常に動作しません(この場合だと正しい情報を入力してもログインに必ず失敗してしまいます)。

これで冒頭で僕が書いた「使いもしないsetterメソッドを書かないといけない疑問」が解消されました。

Struts2がValueStackの中のデータを取り出すために知らない間に使われていたのです。

↓setterメソッドでValueStackからメンバ変数に中身の実体をセットしたイメージ図。

  1. private String xxxx のようにフィールド変数を宣言する
  2. フィールド変数に対応するsetterメソッドを置く

この2つの条件をクリアすれば、JSPからの入力値を自動的に吸い取ってくれる!

↓これで、DAOに対してユーザが入力したユーザ名とパスワードを伝えることができました。
※DAOの処理はValueStackとは直接関係ないので省略します

最後に、ログイン成功後に入力されたユーザ名がJSPファイルに表示される仕組みです。

抱いた疑問は、Actionファイル側で「username変数の中身を送るね!」というコードを書いてないのにJSPファイル側で<s:property value="username"/>と書くだけで受け取れるのはなぜか

<s:property〜>は魔法のタグで、それをJSPファイル側に書くだけでLoginAction.javaファイル内のgetterメソッドを経由して「usernameの中身を引っ張り出す」ということをStruts2が勝手にやってくれます。

ここも、Actionファイル側にgetterを書き忘れるとValueStackから値を取ってこれないので注意が必要です。

これで全て処理が終わり。

JSPは、<s:property value="username">のように書くとActionファイルのgetterメソッドを経由してusernameという変数の中身をgetして表示する

Struts2が、ValueStackという入れ物を介して勝手に値の保持・代入をしてくれていることが分かります。

今回は「テキストボックス」の内容だけを送信する例を書きましたが、JSPファイル側で<s:form>タグで囲めばチェックボックス、ラジオボタン、ドロップダウンリストなどからのデータもValueStackを介してActionファイル側で受け取れます。

注意点おさらい:getter/setterを書き忘れると

getterメソッドを書き忘れると、ログインできるが名前が空欄

LoginAction.javaファイル内のgetterメソッドを書き忘れると、ログインは出来るがログイン後に表示されるJSPファイルでユーザ名が空欄になる、という事態になります。

Struts2のValueStack

●●さん、の「●●」が空欄

JSPファイルの<s:property value="username">の部分は、LoginAction.java内のgetterメソッドを経由してValueStackから値を取ってくるのでしたね。

getterメソッドはあくまでのJSPに表示用の値を渡す役目しかしないため、 ログイン処理自体の成否に影響はしませんがJSPで表示する時に値が取ってこれなくなる、というわけです。

つまりJSPファイルで表示する必要がなければActionファイルにgetterメソッドを書く必要はありません。

慣れるまではgetter/setterを定型文のようにセットで書いてしまうと思いますが、処理のためActionファイルで受け取る(setter)だけでよくJSPに返さない場合はgetterは書かない方が吉です。

全く必要のないものを書く道理はありませんので。

getterを書き忘れると、JSPで値が取れない。では、setterメソッドを書き忘れるとどうなるでしょうか。

setterを忘れるとログイン処理すらできない

LoginAction.javaファイル内のsetterメソッドを書き忘れると、ログイン処理ができずにエラーとなってしまいます。

たとえ正しいユーザ名・パスワードを入力したとしても、です。

すでに使用したイラストを再度見てみましょう。

ValueStackを分かりやすく

DAOがログイン処理でDB検索をするためにはまずユーザが入力したフォームの値をActionファイルが受け取らないといけませんでした。

しかし、setterメソッドがないとStruts2はValueStackにアクセスできないので、Actionファイルのメンバ変数(この例でいう username/password)にフォームから入力された値を代入することができないんですね↓

Struts2のValueStack初心者

なので、JSP側で正しいIDを入力したとしてもLoginAction.javaが値を受け取れず、DAOのログイン処理が正常に動作しないのでログイン結果はエラーとなってしまう、というカラクリです。

getter/setterをアクションファイル内に記載する理由を理解していないと書き忘れてエラーが起きる、なんてことがよく起こります。

これを読んだあなたはもう大丈夫ですね。

Struts2 ValueStackまとめ

  1. ユーザがフォームから入力して送信された値は、一旦ValueStackへ保存される
  2. Struts2が、Actionファイル内のsetterメソッドでValueStackから値をセットする
  3. JSPファイルに<s:property〜>と書くことで、アクションファイル内のgetterメソッドを通して値を取得する

そうとうねちっこく同じような言葉を繰り返してきたので、ValueStackの「映像」が頭に残っているのではないでしょうか?これですよ?これこれ!!

今、あなたはユーザー名とパスワードを入力して送信ボタンを押しました。

ユーザー名・パスワードがValueStackへスポンと格納される「映像」が思い浮かびましたか?

格納された後、Actionファイルに書いたsetterメソッドが起動してValueStackから値を取り出す「映像」が思い浮かびましたか?

JSPファイルに「<s:property value="username"/>」と書いてあったら、Actionファイルのgetterメソッドが起動してValueStackから"username"に注入された値を取り出す「映像」が思い浮かびましたか?

ここまでイメージできるだけでもStruts2の学習速度が間違いなく少しずつ上がってきます。

僕は、記事の中で繰り返し「イメージ」という言葉を使ってきました。プログラミングは目に見えないモノとの戦いだと思いません?自分の見えないところで何がどう動いているか分からないことの連続。僕は、この課題感に「何が何でも絵に描いてみる」という対策をとってみました。そうしたら自分に合ってるな、と気付いたので分からないときはノートに絵を描きます。この記事は、僕が勉強中にノートに落書きしたものが元ネタになってます。絵にすると「コードをタイピングしながら頭にイメージが浮かぶ」という感覚があるので「文字列を書き写すだけの写経」からワンランクアップするためのステップとしておススメ。

以上、長い記事を読んでいただきありがとうございました。

こちらもどうぞ▼
【就職後に困る】駆け出しプログラマよ、Linuxの壁を早く乗り越えろ