1. ホーム
  2. jsf

[解決済み] javax.el.PropertyNotFoundException の識別と解決。ターゲットに到達できない

2022-06-07 08:09:14

質問

ELでマネージドBeanを参照しようとすると、以下のようになります。 #{bean.entity.property} のように参照しようとすると、時々 javax.el.PropertyNotFoundException: Target Unreachable 例外が投げられることがあります。通常、ビーンプロパティが設定されるとき、またはビーンアクションが起動されるときです。

メッセージの種類は5つあるようです。

  1. ターゲットに到達できません。識別子 'bean' は null に解決されました。
  2. ターゲットに到達できず、'entity'がnullに返されました。
  3. ターゲットに到達できず、'null' が null を返しました。
  4. ターゲットに到達できず、''0''が返され、nullが返される
  5. ターゲットに到達できず、'BracketSuffix' が null で返されました。

これらはすべて何を意味するのでしょうか。どのように引き起こされ、どのように解決すべきなのでしょうか?

どのように解決するのですか?

1. ターゲットに到達できません。識別子 'bean' は null に解決されました。

これは、管理されたビーンインスタンス自体が、以下のようなELでまさにその識別子(管理されたビーン名)で見つけることができなかったということに帰結します。 #{bean} .

原因の特定は、3つのステップに分けることができます。

a. 誰がそのビーンを管理しているのか?

b. (デフォルトの)管理されたビーン名は何ですか?

c. バッキングビーンクラスはどこですか?

1a. 誰がビーンを管理しているのか?

最初のステップは、どのビーン管理フレームワークがビーンインスタンスの管理を担っているかを確認することでしょう。それは CDI を経由して @Named ? それとも JSF を経由して @ManagedBean ? それとも を経由して @Component ? 全く同じバッキングビーンクラスに複数のビーン管理フレームワーク固有のアノテーションを混在させていないことを確認できますか?例えば @Named @ManagedBean , @Named @Component または @ManagedBean @Component . これは誤りである。Beanは少なくとも1つのBean管理フレームワークによって管理されなければならず、そのフレームワークは適切に構成されていなければなりません。どれを選べばいいのかすでにわからない場合は、次のサイトへ向かってください。 Backing Bean (@ManagedBean) か CDI Beans (@Named) か? Spring JSFの統合:JSFマネージドBeanにSpringコンポーネント/サービスを注入する方法は?

万が一、それが CDI でビーンを管理している人が @Named を使用している場合、次のことを確認する必要があります。

  • CDI 1.0 (Java EE 6) では、CDI 1.0 に対応するために /WEB-INF/beans.xml ファイルが必要です。これは 空の または以下の内容を含むことができます。

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://java.sun.com/xml/ns/javaee" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                                 http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
      </beans>
    
    
  • CDI 1.1 (Java EE 7) を一切使用せずに beans.xml がない場合、あるいは空の beans.xml ファイル、あるいは上記の CDI 1.0 互換の beans.xml は、CDI 1.0と同じ動作をします。CDI 1.1互換の beans.xml があり、明示的に version="1.1" を指定した場合、デフォルトでは @Named ビーンズ のような明示的なCDIスコープアノテーションは @RequestScoped , @ViewScoped , @SessionScoped , @ApplicationScoped など。明示的なCDIスコープを持たないものも含め、すべてのビーンをCDIマネージドビーンとして登録したい場合は、以下のCDI 1.1互換の /WEB-INF/beans.xmlbean-discovery-mode="all" を設定します(デフォルトは bean-discovery-mode="annotated" ).

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
                                 http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
             version="1.1" bean-discovery-mode="all">
      </beans>
    
    
  • CDI 1.1+を使用する場合 bean-discovery-mode="annotated" (デフォルト) を使っている場合、誤ってJSFスコープをインポートしていないか確認してください。 javax.faces.bean.RequestScoped CDI スコープの代わりに javax.enterprise.context.RequestScoped . IDEのオートコンプリートで気をつけること。

  • Mojarra 2.3.0-2.3.2 と CDI 1.1+ を使っているときに bean-discovery-mode="annotated" (デフォルト) を使用している場合、Mojarra を 2.3.3 以降のバージョンにアップグレードする必要があります。 バグ . アップグレードができない場合は、以下のいずれかの方法で bean-discovery-mode="all"beans.xml とするか、JSF2.3特有の @FacesConfig アノテーションを WAR 内の任意のクラス (一般的にはアプリケーションスコープのスタートアップクラスのようなもの) に付けることができます。

  • Servlet 4.0 コンテナで JSF 2.3 を使用する場合、そのコンテナ内で web.xml で宣言されたServlet 4.0コンテナでJSF 2.3を使う場合、JSF 2.3特有の @FacesConfig アノテーションを WAR 内の任意のクラス (一般的にはアプリケーションスコープのスタートアップクラスのようなもの) に明示的に付ける必要があります。これは、Servlet 3.xでは必要ありません。

  • CDI 3.0を使用する場合、最初のバージョンでパッケージ名が javax.* から jakarta.* というように、すべてのデプロイメントディスクリプタファイルが beans.xml , web.xml , faces-config.xml は適合する 新しい jakartaee スキーマ に準拠しないので、古い javaee スキーマに準拠しません。

      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee 
                                 https://jakarta.ee/xml/ns/jakartaee/beans_3_0.xsd"
             version="3.0" bean-discovery-mode="all">
      </beans>
    
    
  • TomcatやJettyのような非JEEコンテナは、CDIをバンドルして出荷されません。手動でインストールする必要があります。これは、単にライブラリJAR(複数可)を追加するよりも少し手間がかかります。Tomcat については、この回答の指示に従っていることを確認してください。 TomcatにCDIをインストールして使用する方法は?

  • ランタイムのクラスパスがクリーンで、CDI API関連のJARに重複がないこと。複数のCDI実装(Weld、OpenWebBeansなど)が混在していないことを確認してください。ターゲットコンテナがすでにCDI APIをバンドルしている場合、別のCDI、あるいはJava EE APIのJARファイルをwebappと共に提供しないことを確認してください。

  • JSFビューのCDIマネージドBeanをJARでパッケージングしている場合、そのJARは少なくとも有効な /META-INF/beans.xml があることを確認してください (これは空のままでもかまいません)。


万が一、それが JSF を介してBeanを管理している人が、2.3以降非推奨の @ManagedBean で、CDIに移行できない場合、次のことを確認する必要があります。

  • faces-config.xml ルート宣言は、JSF 2.0 と互換性があります。そのため、XSD ファイルと version 少なくとも は JSF 2.0 以上を指定してください。

      <faces-config
          xmlns="http://java.sun.com/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
          version="2.0">
    
    

    JSF 2.1では、単に 2_02.0 によって 2_12.1 をそれぞれ

    JSF 2.2 以降を使用しているのであれば、必ず xmlns.jcp.org 名前空間の代わりに java.sun.com に置き換えます。

      <faces-config
          xmlns="http://xmlns.jcp.org/xml/ns/javaee"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd"
          version="2.2">
    
    

    JSF 2.3では、単に 2_22.2 によって 2_32.3 をそれぞれ

  • 誤ってインポートしていない javax.annotation.ManagedBean の代わりに javax.faces.bean.ManagedBean . IDE のオートコンプリートでは、Eclipse はリストの最初のアイテムとして間違ったものを自動提案することが知られているので、注意してください。

  • をオーバーライドしていない @ManagedBean をJSF 1.xのスタイルで <managed-bean> のエントリで faces-config.xml のエントリは、全く同じバッキングビーンクラスに、異なるマネージドビーン名で追加されます。こちらは @ManagedBean . で管理されたビーンを登録することは faces-config.xml の登録は、JSF2.0以降では不要なので、削除してください。

  • ランタイムクラスパスがクリーンで、JSF API関連のJARに重複がないこと。複数の JSF 実装 (Mojarra と MyFaces) を混合していないことを確認してください。ターゲットコンテナがすでにJSF APIをバンドルしている場合、別のJSFやJava EE APIのJARファイルをwebappと共に提供しないことを確認してください。以下も参照してください。 JSF wikiページの "JSFのインストール"のセクションを参照してください。 を参照してください。コンテナ自体ではなく、コンテナ・バンドルされた JSF を WAR からアップグレードする場合、ターゲット・コンテナに WAR バンドルされた JSF API/impl を使用するように指示したことを確認します。

  • JSFマネージドビーンをJARでパッケージングする場合、そのJARが少なくともJSF2.0互換の /META-INF/faces-config.xml . また、以下を参照してください。 JARファイルで提供されるJSFマネージドBeanを参照するにはどうすればよいですか?

  • もし、あなたが 実際に を使用していて、アップグレードできないのであれば、ビーンを <managed-bean>faces-config.xml の代わりに @ManagedBean . JSF 2.x ライブラリがないように、プロジェクトのビルドパスを修正することを忘れないでください。 @ManagedBean アノテーションがコンパイルに成功するような混乱を招かないように)。


万が一、それが でビーンを管理している場合 @Component で管理している場合、以下のことを確認する必要があります。

  • Springが以下のようにインストールされ、統合されていること。 のドキュメントに従ってインストールされ、統合されています。 . 重要なのは、少なくともこれを web.xml :

      <listener>
          <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      </listener>
    
    

    そして、これを faces-config.xml :

      <application>
          <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
      </application>
    
    
  • (上記は私が知っているSpringに関すること全てです。私はSpringはやりません。Springに関連すると思われる他の原因、例えばXML設定に関するトラブルがあれば、自由に編集/コメントしてください)


万が一、それが リピータコンポーネント であり、その var 属性で管理されています (例 <h:dataTable var="item"> , <ui:repeat var="item"> , <p:tabView var="item"> など)で、実際に "Target Unreachable, identifier 'item' resolved to null" が発生した場合、以下のことを確認する必要があります。

  • その #{item} が参照されないのは binding アトリビュートで参照されていません。これは binding 属性はビューの構築時に実行され、ビューのレンダリング時には実行されないからです。さらに、コンポーネント ツリーには物理的に 1 つのコンポーネントしかなく、繰り返しラウンドごとに再利用されるだけです。つまり、実際に使用するのは binding="#{bean.component}" の代わりに binding="#{item.component}" . しかし、より良い方法は、ビーンに結合するコンポーネントを完全に取り除き、この方法で解決しようと思った問題のための適切なアプローチを調査したり尋ねたりすることです。また JSFでは'binding'属性はどのように機能するのですか?いつ、どのように使うべきですか?


1b. (デフォルトの)マネージド・ビーン名は何ですか?

第二段階は、登録されたマネージドビーンの名前を確認することです。JSFとSpringでは、以下のような規約があります。 JavaBeansの仕様 を採用していますが、CDIはCDI impl/versionによって例外が発生します。

  • A FooBean のようなバッキングビーンクラスを作成します。

      @Named
      public class FooBean {}
    
    

    は、すべてのビーン管理フレームワークにおいて、デフォルトの管理ビーン名である #{fooBean} を持つことになります。

  • A FOOBean のようなバッキングビーンクラスを作成します。

      @Named
      public class FOOBean {}
    
    

    非修飾クラス名が少なくとも2つの大文字で始まる場合、JSFとSpringでは、非修飾クラス名と全く同じデフォルトのマネージドBean名を持ちます。 #{FOOBean} また,JavaBeansの仕様に適合する。CDIでは、2015年6月以前にリリースされたWeldバージョンでもそうですが、2015年6月以降にリリースされたWeldバージョン(2.2.14/2.3.0.B1/3.0.0.A9)でもOpenWebBeansでは CDI仕様の見落とし . これらのWeldバージョンとすべてのOWBバージョンでは、最初の文字が小文字になっただけの #{fOOBean} .

  • マネージドビーンの名前を明示的に指定した場合 foo のようにします。

      @Named("foo")
      public class FooBean {}
    
    

    あるいは、同じように @ManagedBean(name="foo") または @Component("foo") によってのみ利用可能になります。 #{foo} であり、したがって ではなく によって #{fooBean} .


1c. バッキングビーンクラスはどこだ?

3番目のステップは、バッキングビーンクラスがビルドおよびデプロイされたWARファイルの正しい場所にあるかどうかを再確認することです。実際にコードを書くのに忙しく、ブラウザで F5 をせっせと押していた場合に備えて、プロジェクトとサーバーの完全なクリーニング、再構築、再デプロイ、再起動を適切に実行したことを確認します。それでもダメなら、ビルドシステムがWARファイルを生成するので、それをZIPツールで解凍して検査します。コンパイルされた .class ファイルは、バッキングビーンクラスのパッケージ構造内の /WEB-INF/classes . または、JAR モジュールの一部としてパッケージ化されている場合、コンパイルされた .class ファイルに存在する必要があります。 /WEB-INF/lib に存在しなければならず、例えば EAR の /lib などではありません。

Eclipseを使用している場合、バッキングビーンクラスが src であり、したがって ではなく WebContent であることを確認し プロジェクトを自動的にビルドする が有効になっていることを確認します。Mavenを使用している場合、バッキングビーンクラスが src/main/java にあることを確認し、その結果 ではなく src/main/resources または src/main/webapp .

EJB+WAR(s)でEARの一部としてWebアプリケーションをパッケージングしている場合、バッキングBeanクラスがWARモジュールにあり、したがってEARモジュールにもEJBモジュールにもないことを確認する必要があります。ビジネス層(EJB)は、複数の異なるウェブ層(JSF、JAX-RS、JSP/サーブレットなど)で再利用できるように、ウェブ層(WAR)関連の成果物から解放される必要があります。


2. ターゲットに到達できず、'entity' が null を返しました。

これは要するに のネストされた プロパティ entity のように #{bean.entity.property} が返される null . これは通常、JSFが以下のものを必要とする場合にのみ公開されます。 を設定します。 の値を property の値を入力コンポーネントで設定し、一方 #{bean.entity} が返されるのに対し、実際には null .

<h:inputText value="#{bean.entity.property}" />

あらかじめモデルの実体を @PostConstruct で、あるいは <f:viewAction> メソッド、あるいは add() メソッド、あるいは同じビューで CRUD リストやダイアログを操作する場合には

@Named
@ViewScoped
public class Bean {

    private Entity entity; // +getter (setter is not necessary).

    @Inject
    private EntityService entityService;

    @PostConstruct
    public void init() {
        // In case you're updating an existing entity.
        entity = entityService.getById(entityId);

        // Or in case you want to create a new entity.
        entity = new Entity();
    }

    // ...
}

の重要性については @PostConstruct を使用するビーン管理フレームワークを使用している場合、通常のコンストラクタでこれを行うと失敗します。 プロキシ を使用するビーン管理フレームワークを使用している場合、失敗します。常に @PostConstruct を使ってマネージドビーンのインスタンス初期化をフックしてください(そして @PreDestroy を使用してマネージドビーンのインスタンスの破壊にフックします)。さらに、コンストラクタでは、注入された依存関係にはまだアクセスできないので、以下を参照してください。 コンストラクタで @Inject ビーンにアクセスしようとすると NullPointerException が発生します。 .

万が一 entityId を介して提供される場合 <f:viewParam> を介して提供される場合、あなたは <f:viewAction> の代わりに @PostConstruct . また f:viewAction / preRenderViewとPostConstructはいつ使い分けるのですか?

を保存していることを確認する必要があります。 null にのみ作成している場合、ポストバック時にこのモデルを保持する必要があります。 add() アクションメソッドでのみ作成される場合です。最も簡単なのは、ビュースコープにビーンを置くことでしょう。また 正しいビーンスコープを選択する方法は?


3. ターゲットに到達できず、'null' が null を返しました。

2.と同じ原因ですが、使用されているELの実装が、例外メッセージに表示するプロパティ名を保存する際に、多少バグがあり、最終的に'null'と誤って表示されてしまいます。このため、次のようなネストしたプロパティがある場合、デバッグや修正が少し難しくなります。 #{bean.entity.subentity.subsubentity.property} .

解決策はやはり同じで、問題のネストされたエンティティが null であることを確認すること、すべてのレベルで


4. ターゲットに到達できず、''0''が null を返しました。

これも2と同じ原因ですが、使用されている(古い)ELの実装が例外メッセージの表現にバグを持っているだけです。これは、中括弧を使った記法の場合にのみ現れます。 [] のようにELで #{bean.collection[index]} ここで #{bean.collection} 自体は非 NULL ですが、指定されたインデックスの項目は存在しません。このようなメッセージは、その後、次のように解釈されなければなりません。

ターゲットに到達できません。'collection[0]'はnullを返しました。

解決方法も2.と同じで、コレクションアイテムが利用可能であることを確認することです。


5. ターゲットに到達できず、'BracketSuffix' が null を返しました。

これは実は#4と同じ原因ですが、使用されている(古い)ELの実装が例外メッセージに表示する反復インデックスを保存する際に多少バグがあり、最終的に'BracketSuffix'として不正に公開されたもので、実際には文字である ] . これは、コレクションに複数のアイテムがある場合、デバッグと修正を少し難しくするだけです。


その他の原因として考えられるのは javax.el.PropertyNotFoundException :