◀ 3.

Cannot find bean in any scope (Struts) エラー対処

▶
この記事の要点
  • Struts 1 の JSP で <html:text> などのフォーム部品タグを使ったときに出るエラー
  • 最も多い原因は、フォーム部品タグを <html:form> の外に書いていること
  • <html:form> は開始タグでフォーム Bean を「org.apache.struts.taglib.html.BEAN」という名前でリクエストスコープに置き、終了タグで取り除く。中のタグはそれを探す
  • Tiles やインクルードで部品を別 JSP に分けたとき、その JSP がフォームの範囲外で読み込まれていると同じエラーになる
  • 対処: タグを <html:form> 内に移す、または name 属性で Bean 名を明示する

エラー内容

javax.servlet.jsp.JspException: Cannot find bean org.apache.struts.taglib.html.BEAN in any scope

Struts 1 系(1.2 / 1.3)を使った JSP を表示したときに発生し、画面には HTTP 500 エラーとスタックトレースが表示されます。Tomcat などのログには、どの JSP の何行目で例外が起きたかが出力されるので、まずその行のタグを確認します。

発生条件と原因

html:form タグの内側で使用すべきタグ(例: html:text)を、html:form の外側に記述したときに発生するエラーです。

なぜ html:form の外だとエラーになるのか

Struts 1 の <html:form> タグは、開始タグの処理で次のことを行います。

  1. action 属性のパスから struts-config.xml のアクション定義を探す
  2. そのアクションに紐づくフォーム Bean(ActionForm)をリクエストやセッションから取得し、無ければ生成する
  3. 取得したフォーム Bean を、org.apache.struts.taglib.html.BEAN という名前でリクエストスコープに格納する(終了タグ </html:form> の処理で取り除かれる)

<html:text>、<html:password>、<html:checkbox>、<html:select> などのフォーム部品タグは、name 属性を省略すると、この「org.apache.struts.taglib.html.BEAN」を探して値を取り出します。<html:form> の外ではこの Bean がまだ置かれていない(または終了タグで取り除かれた)ため、「どのスコープにも見つからない」というエラーになります。

原因の一覧

原因確認ポイント
フォーム部品タグが <html:form> の外にあるエラー行のタグが </html:form> より後ろ、または <html:form> より前にないか
インクルードした JSP や Tiles のタイルにフォーム部品があるそのタイル・インクルードが <html:form> の開始タグと終了タグの間で読み込まれているか(ヘッダーやサイドバーのタイルに入力欄を置くと範囲外になりやすい)
HTML の入れ子が崩れているテーブルやカスタムタグの途中で </html:form> が閉じられていないか

対処法

1. フォーム部品タグを html:form の中に移す

html:text のようなタグは、html:form タグの内側に記述します。

<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>

<!-- NG: html:form の外にある -->
<html:text property="userId" />
<html:form action="/login">
  <html:password property="password" />
  <html:submit value="ログイン" />
</html:form>

<!-- OK: すべて html:form の中 -->
<html:form action="/login">
  <html:text property="userId" />
  <html:password property="password" />
  <html:submit value="ログイン" />
</html:form>

taglib の uri は Struts 1.3 の例です。1.2 以前では /WEB-INF/struts-html.tld のように TLD ファイルを指定している場合もあります。

2. 分割した JSP はフォームの範囲内で読み込む

入力欄を共通部品として別 JSP や Tiles のタイルに切り出している場合は、その読み込み(<jsp:include>、<tiles:insert> など)を <html:form> と </html:form> の間に置きます。フォーム全体をレイアウトの外に出すより、フォームを 1 つのタイルにまとめて、その中で入力欄を組み立てる方が構造が分かりやすくなります。

3. name 属性で Bean を明示する

どうしても <html:form> の外で値を表示したい場合は、name 属性でスコープ上の Bean の名前(struts-config.xml の form-bean の name)を明示します。こうすると BEAN キーではなく指定した名前の Bean を探します。この場合、その Bean がリクエストまたはセッションに存在している必要があります。

<html:text name="loginForm" property="userId" />

値を表示するだけで入力欄が不要なら、<bean:write name="loginForm" property="userId" /> を使う方が意図に合っています。

よく似たエラーとの見分け方

<html:form> 自体の設定に問題があるときは、このエラーではなく別のメッセージになります。エラーメッセージの文言で切り分けると原因に早くたどり着けます。

メッセージ主な原因
Cannot find bean org.apache.struts.taglib.html.BEAN in any scopeフォーム部品タグが <html:form> の範囲外にある(この記事)
Cannot retrieve mapping for action ...<html:form> の action 属性のパスが struts-config.xml の action 定義に存在しない
Cannot retrieve definition for form bean ...action 定義の name 属性で指定したフォーム Bean が form-beans に定義されていない
Cannot find bean ... in any scope(BEAN 以外の名前)<bean:write> などの name 属性で指定した Bean がリクエストやセッションに無い。Action クラスで request.setAttribute() しているか確認する

最後の行のように、BEAN 以外の名前が表示されている場合は「タグの位置」ではなく「Action 側で Bean を用意していない」ことが原因です。画面に直接アクセスした(.do を経由せず JSP の URL を開いた)ために Action が実行されていない、というケースもよくあります。

確認方法

  1. ログのスタックトレースで、エラーが出た JSP のファイル名と行番号を確認する
  2. その行のタグと、<html:form> の開始・終了位置の関係を確認する(インクルード元・先の両方)
  3. 修正後、アプリケーションサーバーを再起動するか JSP の再コンパイルを待ってから再表示する
  4. Tomcat の work ディレクトリに古いコンパイル結果が残っている場合は削除してから確認する

補足: Struts 1 の現状

Struts 1 は 2013 年にサポート終了(End of Life)が告知されており、セキュリティ修正は提供されていません。既存システムの保守でこのエラーに出会うことが多いはずですが、改修の機会があれば Spring MVC など保守されているフレームワークへの移行も検討してください。

関連

Post Share
子ページ

子ページはありません

同階層のページ
  1. createActionFormにてNullPointerException
  2. java.lang.NullPointerException: Module 'null' not found
  3. javax.servlet.jsp.JspException: Cannot find bean org.apache.struts.taglib.html.BEAN in any scope
  4. Description Resource Path Location Type cvc-complex-type.2.4.a: Invalid content was found starting with element 'X'
  5. パス ... に対するアクションのインスタンスがありません
  6. TLDによると、タグ form の属性 id は無効です