1.目标不可达,标识符“bean”解析为空
这归结为托管 bean 实例本身无法通过 EL 中的标识符(托管 bean 名称)找到,例如 #{bean}。
找出原因可以分为三个步骤:
一个。谁在管理 bean?
湾。什么是(默认)托管 bean 名称?
C。支持 bean 类在哪里?
1a.谁在管理 bean?
第一步是检查哪个 bean 管理框架负责管理 bean 实例。是通过@Named CDI 吗?还是通过@ManagedBean JSF?还是通过@Component 春天?你能确保你没有在同一个支持 bean 类上混合多个 bean 管理框架特定的注释吗?例如。 @Named @ManagedBean、@Named @Component 或 @ManagedBean @Component。这是错误的。 bean 必须由最多一个 bean 管理框架管理,并且该框架必须正确配置。如果您已经不知道该选择哪个,请前往Backing beans (@ManagedBean) or CDI Beans (@Named)? 和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) 没有任何beans.xml,或一个空的beans.xml 文件,或与上述CDI 1.0 兼容的beans.xml 的行为与CDI 1.0 相同。当 CDI 1.1 兼容 beans.xml 带有显式 version="1.1" 时,默认情况下它只会注册 @Named bean with 显式 CDI 范围注释,例如 @RequestScoped、@ViewScoped、 @SessionScoped、@ApplicationScoped 等。如果您打算将所有 bean 注册为 CDI 托管 bean,即使是那些没有显式 CDI 范围的 bean,请使用以下 CDI 1.1 兼容 /WEB-INF/beans.xml 并设置 bean-discovery-mode="all"(默认为 @ 987654379@)。
<?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 自动完成功能。
-
当使用带有bean-discovery-mode="annotated"(默认)的 Mojarra 2.3.0-2.3.2 和 CDI 1.1+ 时,由于bug,您需要将 Mojarra 升级到 2.3.3 或更高版本。如果无法升级,则需要在beans.xml 中设置bean-discovery-mode="all",或者将JSF 2.3 特定的@FacesConfig 注释放在WAR 中的任意类上(通常是某种应用程序范围的启动类)。
-
当在 Servlet 4.0 容器上使用 JSF 2.3 并声明 web.xml 符合 Servlet 4.0 时,您需要显式地将 JSF 2.3 特定的 @FacesConfig 注释放在 WAR 中的任意类上(通常是某种应用程序范围启动类)。这在 Servlet 3.x 中不是必需的。
-
使用 CDI 3.0 时,第一个版本的包从 javax.* 重命名为 jakarta.*,则需要确保所有部署描述符文件 beans.xml、web.xml、faces-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>
-
Tomcat 和 Jetty 等非 JEE 容器不附带 CDI。您需要手动安装它。这比仅添加库 JAR 需要更多的工作。对于 Tomcat,请确保遵循此答案中的说明:How to install and use CDI on Tomcat?
-
您的运行时类路径是干净的,并且在与 CDI API 相关的 JAR 中没有重复项。确保您没有混合多个 CDI 实现(Weld、OpenWebBeans 等)。当目标容器已经捆绑了 CDI API 时,请确保不要在 webapp 中提供另一个 CDI 甚至 Java EE API JAR 文件。
-
如果您在 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_0 和 2.0 分别替换为 2_1 和 2.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_2 和 2.2 分别替换为 2_3 和 2.3。
-
您没有意外导入javax.annotation.ManagedBean 而不是javax.faces.bean.ManagedBean。注意 IDE 自动完成功能,众所周知,Eclipse 会自动提示错误的选项作为列表中的第一项。
-
您没有通过 JSF 1.x 样式的 <managed-bean> 条目覆盖同一支持 bean 类上的 faces-config.xml 条目以及不同的托管 bean 名称。这个优先于@ManagedBean。从 JSF 2.0 开始,不需要在 faces-config.xml 中注册托管 bean,只需将其删除即可。
-
您的运行时类路径是干净的,并且在与 JSF API 相关的 JAR 中没有重复项。确保您没有混合多个 JSF 实现(Mojarra 和 MyFaces)。当目标容器已经捆绑了 JSF API 时,请确保不要在 webapp 中提供另一个 JSF 甚至 Java EE API JAR 文件。有关 JSF 安装说明,另请参阅 "Installing JSF" section of our JSF wiki page。如果您打算从 WAR 而非容器本身升级容器捆绑的 JSF,请确保您已指示目标容器使用 WAR 捆绑的 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,并且无法升级,那么您需要通过 <managed-bean> 在 faces-config.xml 中注册 bean 而不是 @ManagedBean .不要忘记修复您的项目构建路径,使您不再拥有 JSF 2.x 库(这样@ManagedBean 注释就不会成功编译)。
如果是 Spring 通过@Component 管理 bean,那么您需要确保以下几点:
-
正在按照its documentation 安装和集成 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"> 等)管理(嵌套)bean,而您实际上得到了一个“Target Unreachable, identifier 'item' resolve to null”,那么你需要确保以下几点:
1b.什么是(默认)托管 bean 名称?
第二步是检查注册的托管 bean 名称。 JSF 和 Spring 使用约定符合 JavaBeans specification,而 CDI 有例外,具体取决于 CDI impl/version。
-
如下所示的FooBean 支持 bean 类,
@Named
public class FooBean {}
根据 JavaBeans 规范,所有 bean 管理框架都会有一个默认的托管 bean 名称 #{fooBean}。
-
如下所示的FOOBean 支持 bean 类,
@Named
public class FOOBean {}
在 JSF 和 Spring 中,其非限定类名以至少两个大写字母开头的默认托管 bean 名称与非限定类名 #{FOOBean} 完全相同,也符合 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 类在哪里?
第三步是仔细检查支持 bean 类是否在构建和部署的 WAR 文件中的正确位置。确保您已正确执行项目和服务器的完全清理、重建、重新部署和重新启动,以防您实际上正忙于编写代码并在浏览器中不耐烦地按 F5。如果仍然无效,让构建系统生成一个 WAR 文件,然后使用 ZIP 工具提取和检查该文件。已编译的支持 bean 类的.class 文件必须驻留在其包结构/WEB-INF/classes 中。或者,当它被打包为 JAR 模块的一部分时,包含已编译的 .class 文件的 JAR 必须驻留在 /WEB-INF/lib 中,因此不是例如EAR 的 /lib 或其他地方。
如果您使用的是 Eclipse,请确保支持 bean 类位于 src 中,因此不是 WebContent,并确保 Project > Build Automatically 已启用。如果您使用 Maven,请确保支持 bean 类位于 src/main/java 中,因此 不 位于 src/main/resources 或 src/main/webapp 中。
如果您使用 EJB+WAR(s) 将 Web 应用程序打包为 EAR 的一部分,那么您需要确保支持 bean 类在 WAR 模块中,因此不在 EAR 模块或 EJB 模块中。业务层 (EJB) 必须没有任何与 Web 层 (WAR) 相关的工件,以便业务层可跨多个不同的 Web 层(JSF、JAX-RS、JSP/Servlet 等)重用。
2.目标不可达,“实体”返回 null
这归结为#{bean.entity.property} 中的嵌套 属性#{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的重要性;如果您使用的是使用proxies 的bean 管理框架,例如CDI,那么在常规构造函数中执行此操作会失败。始终使用@PostConstruct 挂钩托管bean 实例初始化(并使用@PreDestroy 挂钩托管bean 实例销毁)。此外,在构造函数中,您还无法访问任何注入的依赖项,另请参阅 NullPointerException while trying to access @Inject bean in constructor。
如果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} 本身不为空,但指定索引处的项目不存在。然后必须将这样的消息解释为:
目标不可达,'collection[0]' 返回 null
解决方法也同#2:确保收藏项可用。
5.目标不可达,'BracketSuffix' 返回 null
这实际上与 #4 的原因相同,只是使用的(较旧的)EL 实现在保留迭代索引以显示在异常消息中有些错误,最终错误地暴露为“BracketSuffix”,这实际上是字符]。当集合中有多个项目时,这只会使调试和修复变得更加困难。
javax.el.PropertyNotFoundException 的其他可能原因: