识别和解决 javax.el.PropertyNotFoundException:目标无法到达

Identifying and solving javax.el.PropertyNotFoundException: Target Unreachable

当尝试像这样 #{bean.entity.property} 引用 EL 中的托管 bean 时,有时会抛出 javax.el.PropertyNotFoundException: Target Unreachable 异常,通常是在要设置 bean 属性 时,或者当要调用 bean 操作。

似乎有五种不同的消息:

  1. Target Unreachable, identifier 'bean' resolved to null
  2. Target Unreachable, 'entity' returned null
  3. Target Unreachable, 'null' returned null
  4. Target Unreachable, ''0'' returned null
  5. Target Unreachable, 'BracketSuffix' returned null

它们都是什么意思?它们是如何引起的,又该如何解决?

1。目标无法到达,标识符 'bean' 解析为 null

这归结为托管 bean 实例本身无法通过 EL 中的那个标识符(托管 bean 名称)找到,就像这样 #{bean}

确定原因可分为三个步骤:

一个。谁在管理 bean?
b.什么是(默认)托管 bean 名称?
C。支持 bean 在哪里 class?

1a。谁在管理这个 bean?

第一步是检查哪个 bean 管理框架负责管理 bean 实例。是 CDI 通过 @Named? Or is it JSF via @ManagedBean? Or is it Spring via @Component? Can you make sure that you're not mixing multiple bean management framework specific annotations on the very same backing bean class? E.g. @Named @ManagedBean, @Named @Component, or @ManagedBean @Component. This is wrong. The bean must be managed by at most one bean management framework and that framework must be properly configured. If you already have no idea which to choose, head to Backing beans (@ManagedBean) or CDI Beans (@Named)? and Spring JSF integration: how to inject a Spring component/service in JSF managed bean?

如果 CDI 通过 @Named 管理 bean,那么您需要确保以下内容:

  • CDI 1.0 (Java EE 6) 需要 /WEB-INF/beans.xml 文件才能在 WAR 中启用 CDI。它可以是 或者它可以只有以下内容:

      <?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) without any beans.xml, or an empty beans.xml file, or with the above CDI 1.0 compatible beans.xml will behave the same as CDI 1.0. When there's a CDI 1.1 compatible beans.xml with an explicit version="1.1", then it will by default only register @Named beans with an explicit CDI scope annotation such as @RequestScoped, @ViewScoped, @SessionScoped, @ApplicationScoped,等等。如果您打算将所有 bean 注册为 CDI 托管 bean,即使是那些没有明确 CDI 范围的 bean,请使用以下 CDI 1.1 兼容 /WEB-INF/beans.xml bean-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 instead of a CDI scope javax.enterprise.context.RequestScoped。注意 IDE 自动完成。

  • 当使用 Mojarra 2.3.0-2.3.2 和 CDI 1.1+ bean-discovery-mode="annotated"(默认)时,您需要将 Mojarra 升级到 2.3.3 或更新版本,因为 bug。如果您无法升级,那么您需要在 beans.xml 中设置 bean-discovery-mode="all",或者将 JSF 2.3 特定的 @FacesConfig 注释放在 class 中的任意 class =453=](通常是某种应用程序范围内的启动 class)。

  • 当在 Servlet 4.0 容器上使用 JSF 2.3 并声明 web.xml 符合 Servlet 4.0 时,您需要明确地将 JSF 2.3 特定的 @FacesConfig 注释放在任意class 在 WAR 中(通常是某种应用程序范围内的启动 class)。这在 Servlet 3.x.

    中是没有必要的
  • 当使用CDI 3.0时,第一个版本的package从javax.*重命名为jakarta.*,那么你需要确保所有部署描述符文件beans.xmlweb.xmlfaces-config.xml 符合 the new jakartaee schemas,因此不符合旧的 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>
    
  • Non-JEE 容器,如 Tomcat 和 Jetty 不捆绑 CDI。您需要手动安装它。这比仅仅添加库 JAR 多了一些工作。对于 Tomcat,请确保您遵循此答案中的说明:How to install and use CDI on Tomcat?

  • 您的运行时 class 路径是干净的,并且在 CDI API 相关 JAR 中没有重复项。确保您没有混合使用多个 CDI 实现(Weld、OpenWebBeans 等)。当目标容器已经捆绑了 CDI API 开箱即用时,请确保您不提供另一个 CDI 甚至 Java EE API JAR 文件以及 webapp。

  • 如果您在 JAR 中为 JSF 视图打包 CDI 托管 bean,请确保 JAR 至少具有有效的 /META-INF/beans.xml(可以保留为空)。


如果 JSF 通过自 2.3 起已弃用的 @ManagedBean 管理 bean,而您无法迁移到 CDI,那么您需要确保以下:

  • faces-config.xml 根声明与 JSF 2.0 兼容。因此 XSD 文件和 version 必须 至少 指定 JSF 2.0 或更高版本,因此不能 1.x.

      <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 instead of javax.faces.bean.ManagedBean。注意 IDE 自动完成,众所周知,Eclipse 会自动将错误的建议作为列表中的第一项。

  • 您没有在 faces-config.xml 中的相同支持 bean 上通过 JSF 1.x 样式 <managed-bean> 条目覆盖 @ManagedBean class 以及不同的托管 bean 名称。这个优先于 @ManagedBean。自 JSF 2.0 以来,无需在 faces-config.xml 中注册托管 bean,只需将其删除即可。

  • 您的运行时 class 路径是干净的,并且在 JSF API 相关 JAR 中没有重复项。确保您没有混合使用多个 JSF 实现(Mojarra 和 MyFaces)。当目标容器已经捆绑开箱即用的 JSF API 时,请确保您不提供另一个 JSF 甚至 Java EE API JAR 文件。有关 JSF 安装说明,另请参阅 "Installing JSF" section of our JSF wiki page。如果您打算从 WAR 上而不是容器本身升级 container-bundled JSF,请确保您已指示目标容器使用 WAR-bundled JSF API/impl。

  • 如果您将 JSF 托管 bean 打包到 JAR 中,请确保 JAR 至少具有与 JSF 2.0 兼容的 /META-INF/faces-config.xml。另见 How to reference JSF managed beans which are provided in a JAR file?

  • 如果您 实际上 使用侏罗纪 JSF 1.x,并且您无法升级,那么您需要通过以下方式注册 bean <managed-bean>faces-config.xml 而不是 @ManagedBean。不要忘记修复您的项目构建路径,这样您就不再拥有 JSF 2.x 库(这样 @ManagedBean 注释就不会令人困惑地成功编译)。


如果 Spring 通过 @Component 管理 bean,那么您需要确保以下内容:

  • Spring 正在按照 its documentation 安装和集成。重要的是,你至少需要在 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 — 随意 edit/comment其他可能 Spring 相关的原因;例如一些 XML 配置相关的问题)


如果它是一个 中继器组件,它通过其 var 属性(例如 <h:dataTable var="item"><ui:repeat var="item"> 来管理(嵌套的)bean <p:tabView var="item">, etc) 而你实际上得到了一个 "Target Unreachable, identifier 'item' resolved to null", 那么你需要确定以下几点:

  • #{item} 未在任何 child 组件的 binding 属性中引用。这是不正确的,因为 binding 属性在视图构建期间运行,而不是在视图渲染期间运行。此外,组件树中实际上只有一个组件在每一轮迭代中被简单地重用。换句话说,您实际上应该使用 binding="#{bean.component}" 而不是 binding="#{item.component}"。但更好的方法是完全摆脱组件绑定到 bean 和 investigate/ask 解决您认为以这种方式解决的问题的正确方法。另见 How does the 'binding' attribute work in JSF? When and how should it be used?


1b。什么是(默认)托管 bean 名称?

第二步是检查注册的托管 bean 名称。 JSF 和 Spring 使用约定符合 JavaBeans specification 而 CDI 有例外取决于 CDI impl/version.

  • A​​ FooBean backing bean class 如下所示,

      @Named
      public class FooBean {}
    

    根据 JavaBeans 规范,所有 bean 管理框架都有一个默认的托管 bean 名称 #{fooBean}

  • A​​ FOOBean backing bean class 如下所示,

      @Named
      public class FOOBean {}
    

    其不合格的 class 名称以至少两个大写字母开头将在 JSF 中,并且 Spring 具有完全不合格的 class 名称 #{FOOBean} 的默认托管 bean 名称,也符合 JavaBeans 规范。在 CDI 中,2015 年 6 月之前发布的 Weld 版本也是如此,但 2015 年 6 月之后发布的 Weld 版本(2.2.14/2.3.0.B1/3.0.0.A9)和 OpenWebBeans 都不是,因为 an oversight in CDI spec。在那些 Weld 版本和所有 OWB 版本中,只有第一个字符小写 #{fOOBean}.

  • 如果您明确指定了托管 bean 名称 foo,如下所示,

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

    或等同于 @ManagedBean(name="foo")@Component("foo"),则它只能由 #{foo} 提供,因此 而不是 #{fooBean} ].


1c。支持 bean 在哪里 class?

第三步是仔细检查支持 bean class 是否位于构建和部署的 WAR 文件中的正确位置。确保您已正确执行项目和服务器的完全清理、重建、重新部署和重新启动,以防您实际上忙于编写代码并在浏览器中不耐烦地按 F5。如果仍然无效,让构建系统生成一个 WAR 文件,然后使用 ZIP 工具提取并检查该文件。支持 bean class 的已编译 .class 文件必须驻留在 /WEB-INF/classes 中的包结构中。或者,当它被打包为 JAR 模块的一部分时,包含已编译 .class 文件的 JAR 必须驻留在 /WEB-INF/lib 中,因此不是例如EAR 的 /lib 或其他地方。

如果您使用的是 Eclipse,请确保支持 bean class 在 src 中,因此 而不是 WebContent,并且确保 Project > Build Automatically 已启用。如果您使用的是 Maven,请确保支持 bean class 在 src/main/java 中,因此 而不是 src/main/resourcessrc/main/webapp 中.

如果您使用 EJB+WAR(s) 将 Web 应用程序打包为 EAR 的一部分,那么您需要确保支持 bean classes 在 WAR 模块,因此不在 EAR 模块或 EJB 模块中。业务层 (EJB) 必须没有任何与 Web 层 (WAR) 相关的工件,以便业务层可跨多个不同的 Web 层重用(JSF、JAX-RS、JSP/Servlet、等)。


2。目标无法到达,'entity' 返回 null

归结为 嵌套 属性 entity,如 #{bean.entity.property} 返回 null。这通常仅在 JSF 需要 设置 通过输入组件 property 的值时公开,如下所示,而 #{bean.entity} 实际上返回 null

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

您需要确保您已经预先在 @PostConstruct<f:viewAction> 方法中准备好模型实体,或者可能是 add() 操作方法,以防您正在工作带有 CRUD 列表 and/or 同一视图上的对话框。

@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的重要性;如果您使用的是使用 proxies, such as CDI. Always use @PostConstruct to hook on managed bean instance initialization (and use @PreDestroy to hook on managed bean instance destruction). Additionally, in a constructor you wouldn't have access to any injected dependencies yet, see also NullPointerException while trying to access @Inject bean in constructor.

的 bean 管理框架,那么在常规构造函数中执行此操作将会失败

如果 entityId 是通过 <f:viewParam> 提供的,您需要使用 <f:viewAction> 而不是 @PostConstruct。另见 When to use f:viewAction / preRenderView versus PostConstruct?

您还需要确保在回发期间保留非 null 模型,以防您仅在 add() 操作方法中创建它。最简单的方法是将 bean 放在视图范围内。另见 How to choose the right bean scope?


3。目标无法到达,'null' 返回 null

这实际上与 #2 有相同的原因,只是正在使用的(较旧的)EL 实现在保留 属性 名称以显示在异常消息中时有些错误,最终错误地暴露为 'null'。当你有相当多的嵌套属性时,这只会让调试和修复变得更加困难 #{bean.entity.subentity.subsubentity.property}.

解决方法还是一样:确保所讨论的嵌套实体在所有级别都不是null


4。目标无法到达,“0”返回 null

这也与#2 有相同的原因,只是正在使用的(较旧的)EL 实现在制定异常消息时存在错误。仅当您在 EL 中使用大括号符号 [] 时才会公开,例如 #{bean.collection[index]},其中 #{bean.collection} 本身是 non-null,但指定索引处的项目不存在。这样的消息必须解释为:

Target Unreachable, 'collection[0]' returned null

解决方法也同#2:确保collection项可用。


5。目标无法到达,'BracketSuffix' 返回 null

这实际上与#4 有相同的原因,只是正在使用的(较旧的)EL 实现在保留迭代索引以显示在异常消息中时有些错误,最终错误地暴露为 'BracketSuffix'确实是字符]。当 collection.

中有多个项目时,这只会使调试和修复变得更加困难

javax.el.PropertyNotFoundException的其他可能原因:

对于那些仍然卡住的人...

将 NetBeans 8.1 和 GlassFish 4.1 与 CDI 结合使用,出于某种原因,我只在本地遇到了这个问题,在远程服务器上没有。有什么诀窍:

-> 使用 javaee-web-api 7.0 而不是 NetBeans 默认提供的 pom 版本,即 javaee-web-api 6.0,所以:

<dependency>
    <groupId>javax</groupId>
    <artifactId>javaee-web-api</artifactId>
    <version>7.0</version>
    <scope>provided</scope>
    <type>jar</type>
</dependency>

-> 将此 javaee-web-api-7.0.jar 作为 lib 上传到服务器上(domain1 文件夹中的 lib 文件夹)并重新启动服务器。

以旧方式使用 JSF 您必须在 beans-config.xml 文件(位于 WEB-INF 文件夹中)并在 web.xml 中引用它文件,这样:

豆子-config.xml

<managed-bean>
  <managed-bean-name>"the name by wich your backing bean will be referenced"</managed-bean-name>
  <managed-bean-class>"your backing bean fully qualified class name"</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>    
</managed-bean>

(我试过使用其他示波器,但是...)

web.xml

<context-param>
  <param-name>javax.faces.CONFIG_FILES</param-name>
  <param-value>"/WEB-INF/beans-config.xml</param-value>
</context-param>

另一条线索: 我正在使用 JSF,并添加了 mvn 依赖项: com.sun.faces jsf-api 2.2.11

    <dependency>
        <groupId>com.sun.faces</groupId>
        <artifactId>jsf-impl</artifactId>
        <version>2.2.11</version>
    </dependency>

然后,我尝试换成Primefaces,并添加primefaces依赖:

<dependency>
    <groupId>org.primefaces</groupId>
    <artifactId>primefaces</artifactId>
    <version>6.0</version>
</dependency>

我将我的 xhtml 从 h: 更改为 p:,并将 xmlns:p="http://primefaces.org/ui 添加到模板中。 只有使用 JSF,proyect 运行 ok,并且 managedbean 达到了 ok。当我添加 Primefaces 时,我得到了无法访问的对象 (javax.el.propertynotfoundexception)。问题是 JSF 正在生成 ManagedBean,而不是 Primefaces,而我正在向 primefaces 询问该对象。我不得不从我的 .pom 中删除 jsf-impl,清理并安装项目。 从这一点开始一切顺利。 希望对您有所帮助。

在我的例子中,我在@Named("beanName")中犯了一个拼写错误,它应该是"beanName",但是我写了"beanNam",例如。

我正在为 javaee 容器使用 wildfly 10。我遇到了 "Target Unreachable, 'entity' returned null" 个问题。感谢 BalusC 的建议,但解决方案中的我的问题得到了解释。 不小心使用 "import com.sun.istack.logging.Logger;" 而不是 "import org.jboss.logging.Logger;" 导致 CDI 实现了 JSF EL。 希望它有助于改进解决方案。

我遇到了同样的问题。事实证明,解决方案要简单得多。似乎数据表需要 getter 形式的方法,即 getSomeMethod(),而不仅仅是 someMethod()。在我的例子中,我在数据表中调用了 findResults。我将支持 bean 中的方法更改为 getFindResults() 并且它起作用了。

commandButton 在没有 get 的情况下可以正常工作,这只会让它变得更加混乱。

我决定在自己解决这个错误后分享我的发现。

首先,应该认真对待 BalusC 解决方案,但 Netbeans 中还有另一个可能的问题需要注意,尤其是在使用 Maven 构建 企业应用程序项目 (EAR) 时.

Netbeans 生成一个 父 POM 文件、一个 EAR 项目、一个 EJB 项目和一个 WAR 项目。 我项目中的其他一切都很好,我几乎认为问题可能是 GlassFish 4.0(我必须安装并将其插入 Netbeans)中的一个错误,因为 GlassFish 4.1 有一个 Weld CDI 错误,它使得 Netbeans 8.0 中嵌入了 GlassFish 4.1。 2 无法使用,除非通过补丁。

解法:

解决"Target Unreachable, identifier 'bean' resolved to null" 错误-

I 右键单击​​父 POM 项目,然后 select Properties。出现项目属性对话框,单击 "Sources",您会惊讶地看到“Source/Binary Format”设置为 1.5 和“Encoding" 设置为 Windows 1250。 将“Source/Binary Format”更改为 1.6 0r 1.7,无论您希望使项目 CDI 兼容,并将“Encoding”更改为UTF-8。

对所有其他子项目(EAR、EJB、WAR)执行相同的操作(如果它们尚未兼容)。 运行 您的项目,您将不会再遇到该错误。

我希望这对遇到类似错误的人有所帮助。

我决定分享我的解决方案,因为虽然这里提供的许多答案很有帮助,但我仍然遇到这个问题。在我的例子中,我为我的新项目使用 JSF 2.3、jdk10、jee8、cdi 2.0,我在 wildfly 12 上做了 运行 我的应用程序,用参数 standalone.sh -Dee8.preview.mode= 启动服务器与 Wildfly 网站上推荐的一样。下载 wildfly 13 后,“bean resolved to null”的问题消失了。上传完全相同的 war 到 wildfly 13 就可以了。

EL 按照描述解释 ${bean.propretyName} - 假设您使用显式或隐式方法生成 getter/setters

,则 propertyName 变为 getPropertyName()

您可以通过将名称显式标识为函数来覆盖此行为:${bean.methodName()} 这会直接调用函数方法 Name() 而无需修改。

您的访问器被命名为 "get..." 并不总是正确的。

至于#2,在我的例子中,它在替换

后神奇地复活了
<body>

带有

的标签
<h:body>

在完成了几个(老实说,更简单的)JSF 项目之后,我不记得现在做了什么不同的设置,我第一次遇到这种错误。我正在制作一个非常基本的登录页面(用户名、密码、用户 Bean...)并像往常一样设置所有内容。我发现的唯一区别是前面提到的标签。也许有人觉得这很有用。

在我的案例中,问题是我包含了一个带参数的构造函数,但没有包含带有 Inject 注释的空构造函数,就像这样。

@Inject public VisitorBean() {}

我只是在没有任何构造函数的情况下对其进行了测试,这似乎也有效。

对于 1. 主题(目标无法访问,标识符 'bean' 解析为 null);

我检查了@BalusC 和其他共享者的有价值的答案,但在我的场景中我超出了这样的问题。 在创建一个具有不同名称的新 xhtml 并创建具有不同名称的 bean class 之后,我将代码逐步写入(不是 copy-paste)新 bean class 和新 xhtml 文件。

我被这个错误卡住了,因为在 class 中有 @SpringBootApplication 我忘记指定控制器的包名称。

这次我想更具体地指出哪些组件 Spring 必须扫描,而不是配置基本包。

原来是这样的:

@ComponentScan(basePackages = {"br.com.company.project.repository", "br.com.company.project.service"})

但正确的形式是以下之一:

@ComponentScan(basePackages = {"br.com.company.project.repository", "br.com.company.project.service", "br.com.company.project.controller"})

@ComponentScan(basePackages = {"br.com.company.project")

我决定分享我的解决方案,因为虽然正确答案非常全面,但没有涵盖这个(愚蠢的)错误:)

当我从 web.xml 文件中删除 AnnotationConfigWebApplicationContext 上下文参数时这是有效的

如果您有如下所示的类似参数,您必须将其从 web.xml 文件中删除

<context-param>
    <param-name>contextClass</param-name>
    <param-value>
      org.springframework.web.context.support.AnnotationConfigWebApplicationContext
  </param-value>
</context-param> 

在我的例子中 "el-ri-1.0.jar" 丢失了。

它也可能是 Mojarra 2.3 中的一个错误https://github.com/eclipse-ee4j/mojarra/issues/4734

首先,我使用的是:Eclipse、Weblogic、CDI、JSF、PrimeFaces。如果你也是,也许我的解决方案可以帮到你。

就我而言,错误的原因是“Eclipse”上的一些设置。

检查这个:

  1. 在“服务器”选项卡上右键单击您的 Weblogic 服务器
  2. Select“属性”
  3. 在属性的新小 window 中,展开“Weblogic”菜单
  4. 在“Weblogic”菜单中,点击“发布”选项
  5. 现在,确保选中右侧的选项“发布为展开的存档”。

在我的例子中,我选中了“作为虚拟应用程序发布”,因此,更改后我解决了“目标无法访问”错误。