识别和解决javax.el.PropertyNotFoundException:目标不可访问 [英] Identifying and solving javax.el.PropertyNotFoundException: Target Unreachable

查看:117
本文介绍了识别和解决javax.el.PropertyNotFoundException:目标不可访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

When trying to reference a managed bean in EL like so #{bean.entity.property}, sometimes a javax.el.PropertyNotFoundException: Target Unreachable exception is being thrown, usually when a bean property is to be set, or when a bean action is to be invoked.

似乎有五种不同的消息:

There seem to be five different kinds of messages:

  1. 目标无法访问,标识符"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

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

What do they all mean? How are they caused and how should they be solved?

推荐答案

1.目标无法到达,标识符"bean"解析为空

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

1. Target Unreachable, identifier 'bean' resolved to null

This boils down to that the managed bean instance itself could not be found by exactly that identifier (managed bean name) in EL like so #{bean}.

找出原因可以分为三个步骤:

Identifying the cause can be broken down into three steps:

a.谁在管理bean?
b. (默认)托管bean名称是什么?
C.支持bean类在哪里?

a. Who's managing the bean?
b. What's the (default) managed bean name?
c. Where's the backing bean class?

第一步是检查哪个bean管理框架负责管理bean实例.是否通过 ?还是通过 @Named CDI ?还是通过 Spring ="noreferrer"> @Component ?您能确定不会在同一个支持bean类上混合使用多个特定于bean管理框架的注释吗?例如. @Named @Component@Named @ManagedBean@ManagedBean @Component.这是错误的.该bean必须最多由一个bean管理框架管理,并且该框架必须正确配置.如果您不知道要选择哪个,请转至支持bean (@ManagedBean)还是CDI Bean(@Named)?

First step would be checking which bean management framework is responsible for managing the bean instance. Is it JSF via @ManagedBean? Or is it CDI via @Named? 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 @Component, or @Named @ManagedBean, 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?

如果是 JSF 通过@ManagedBean管理Bean,那么您需要确保以下几点:

In case it's JSF who's managing the bean via @ManagedBean, then you need to make sure of the following:

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

  • The faces-config.xml root declaration is compatible with JSF 2.0. So the XSD file and the version must at least specify JSF 2.0 or higher and thus not 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.

For JSF 2.1, just replace 2_0 and 2.0 by 2_1 and 2.1 respectively.

如果您使用的是JSF 2.2或更高版本,请确保在所有位置都使用xmlns.jcp.org命名空间而不是java.sun.com.

If you're on JSF 2.2 or higher, then make sure you're using xmlns.jcp.org namespaces instead of java.sun.com over all place.

<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.

For JSF 2.3, just replace 2_2 and 2.2 by 2_3 and 2.3 respectively.

您没有意外导入 javax.annotation.ManagedBean 代替 javax.faces.bean.ManagedBean .借助IDE自动完成功能当心,众所周知,Eclipse会自动建议错误的列表中的第一项.

You didn't accidentally import javax.annotation.ManagedBean instead of javax.faces.bean.ManagedBean. Watch out with IDE autocomplete, Eclipse is known to autosuggest the wrong one as first item in the list.

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

If you're actually using the jurassic JSF 1.x, and you can't upgrade, then you need to register the bean via <managed-bean> in faces-config.xml instead of @ManagedBean. Don't forget to fix your project build path as such that you don't have JSF 2.x libraries anymore (so that the @ManagedBean annotation wouldn't confusingly successfully compile).

如果是 CDI 通过@Named管理Bean,那么您需要确保以下几点:

In case it's CDI who's managing the bean via @Named, then you need to make sure of the following:

  • CDI 1.0(Java EE 6)需要一个/WEB-INF/beans.xml文件,以便在WAR中启用CDI.它可以是 empty ,也可以只有以下内容:

  • CDI 1.0 (Java EE 6) requires an /WEB-INF/beans.xml file in order to enable CDI in WAR. It can be empty or it can have just the following content:

<?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"时,默认情况下,它将仅在显式CDI范围注释(例如(默认为bean-discovery-mode="annotated").

  • 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, etc. In case you intend to register all beans as CDI managed beans, even those without an explicit CDI scope, use the below CDI 1.1 compatible /WEB-INF/beans.xml with bean-discovery-mode="all" set (the default is 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范围,例如

  • When using CDI 1.1+ with bean-discovery-mode="annotated" (default), make sure that you didn't accidentally import a JSF scope such as javax.faces.bean.RequestScoped instead of a CDI scope javax.enterprise.context.RequestScoped. Watch out with IDE autocomplete.

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

    If you're packaging CDI managed beans for JSF views in a JAR, then make sure that the JAR has at least a valid /META-INF/beans.xml (which can be kept empty).

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

    In case it's Spring who's managing the bean via @Component, then you need to make sure of the following:

    • Spring is being installed and integrated as per its documentation. Importantingly, you need to at least have this in 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. 无法到达目标,标识符'item'解析为null",那么您需要确保以下内容:

    In case it's a repeater component who's managing the (nested) bean via its var attribute (e.g. <h:dataTable var="item">, <ui:repeat var="item">, <p:tabView var="item">, etc) and you actually got a "Target Unreachable, identifier 'item' resolved to null", then you need to make sure of the following:

    • The #{item} is not referenced in binding attribtue of any child component. This is incorrect as binding attribute runs during view build time, not during view render time. Moreover, there's physically only one component in the component tree which is simply reused during every iteration round. In other words, you should actually be using binding="#{bean.component}" instead of binding="#{item.component}". But much better is to get rid of component bining to bean altogether and investigate/ask the proper approach for the problem you thought to solve this way. See also How does the 'binding' attribute work in JSF? When and how should it be used?

    第二步将检查已注册的受管Bean名称. JSF和Spring使用约定遵循 JavaBeans规范,而CDI依赖于异常在CDI展示次数/版本上.

    Second step would be checking the registered managed bean name. JSF and Spring use conventions conform JavaBeans specification while CDI has exceptions depending on CDI impl/version.

    • 一个FooBean支持bean类,如下所示,

    • A FooBean backing bean class like below,

    @Named
    public class FooBean {}
    

    根据JavaBeans规范,

    在所有bean管理框架中的默认托管bean名称为#{fooBean}.

    will in all bean management frameworks have a default managed bean name of #{fooBean}, as per JavaBeans specification.

    一个FOOBean支持bean类,如下所示,

    A FOOBean backing bean class like below,

    @Named
    public class FOOBean {}
    

    其不合格的类名至少以两个大写字母开头,在JSF和Spring中将具有与不合格的类名#{FOOBean}完全相同的默认受管Bean名称,并且也符合JavaBeans规范.在CDI中,2015年6月之前发布的Weld版本中也是如此,但由于对CDI规范的监督.在那些Weld版本和所有OWB版本中,只有第一个字符小写#{fOOBean}.

    whose unqualified classname starts with at least two capitals will in JSF and Spring have a default managed bean name of exactly the unqualified class name #{FOOBean}, also conform JavaBeans specificiation. In CDI, this is also the case in Weld versions released before June 2015, but not in Weld versions released after June 2015 (2.2.14/2.3.0.B1/3.0.0.A9) nor in OpenWebBeans due to an oversight in CDI spec. In those Weld versions and in all OWB versions it is only with the first character lowercased #{fOOBean}.

    如果您像下面那样显式指定了托管Bean名称foo

    If you have explicitly specified a managed bean name foo like below,

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

    或与@ManagedBean(name="foo")@Component("foo")等效的产品,则只能由#{foo}使用,因此不能#{fooBean}使用.

    or equivalently with @ManagedBean(name="foo") or @Component("foo"), then it will only be available by #{foo} and thus not by #{fooBean}.

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

    Third step would be doublechecking if the backing bean class is at the right place in the built and deployed WAR file. Make sure that you've properly performed a full clean, rebuild, redeploy and restart of the project and server in case you was actually busy writing code and impatiently pressing F5 in the browser. If still in vain, let the build system produce a WAR file, which you then extract and inspect with a ZIP tool. The compiled .class file of the backing bean class must reside in its package structure in /WEB-INF/classes. Or, when it's packaged as part of a JAR module, the JAR containing the compiled .class file must reside in /WEB-INF/lib and thus not e.g. EAR's /lib or elsewhere.

    如果您使用的是Eclipse,请确保后备bean类位于src中,因此位于不是 WebContent中,并确保 Project>自动构建已启用.如果您使用的是Maven,请确保后备bean类位于src/main/java中,因此不是src/main/resourcessrc/main/webapp中.

    If you're using Eclipse, make sure that the backing bean class is in src and thus not WebContent, and make sure that Project > Build Automatically is enabled. If you're using Maven, make sure that the backing bean class is in src/main/java and thus not in src/main/resources or src/main/webapp.

    如果要使用EJB + WAR将Web应用程序打包为EAR的一部分,则需要确保后备Bean类位于WAR模块中,因此不在EAR模块或EJB模块中.业务层(EJB)必须没有任何与Web层(WAR)相关的工件,以便该业务层可在多个不同的Web层(JSF,JAX-RS,JSP/Servlet等)中重用.

    If you're packaging the web application as part of an EAR with EJB+WAR(s), then you need to make sure that the backing bean classes are in WAR module and thus not in EAR module nor EJB module. The business tier (EJB) must be free of any web tier (WAR) related artifacts, so that the business tier is reusable across multiple different web tiers (JSF, JAX-RS, JSP/Servlet, etc).

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

    This boils down to that the nested property entity as in #{bean.entity.property} returned null. This usually only exposes when JSF needs to set the value for property via an input component like below, while the #{bean.entity} actually returned null.

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

    如果要使用CRUD列表和/或对话框,则需要确保预先使用@PostConstruct<f:viewAction>方法,或者也许是add()动作方法准备了模型实体.相同的视图.

    You need to make sure that you have prepared the model entity beforehand in a @PostConstruct, or <f:viewAction> method, or perhaps an add() action method in case you're working with CRUD lists and/or dialogs on same view.

    @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的重要性;如果您正在使用使用尝试访问构造函数中的@Inject bean时发生NullPointerException .

    As to the importance of @PostConstruct; doing this in a regular constructor would fail in case you're using a bean management framework which uses 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.

    如果通过<f:viewParam>提供entityId,则需要使用<f:viewAction>而不是@PostConstruct.另请参见何时使用f:viewAction/preRenderView与PostConstruct?

    In case the entityId is supplied via <f:viewParam>, you'd need to use <f:viewAction> instead of @PostConstruct. See also When to use f:viewAction / preRenderView versus PostConstruct?

    您还需要确保在回发期间保留非null模型,以防仅在add()动作方法中创建它.最简单的方法是将bean放入视图范围.另请参见如何选择正确的bean作用域?

    You also need to make sure that you preserve the non-null model during postbacks in case you're creating it only in an add() action method. Easiest would be to put the bean in the view scope. See also How to choose the right bean scope?

    这实际上与#2具有相同的原因,只是所使用的(较旧的)EL实现在保留要显示在异常消息中的属性名称时有些错误,最终错误地将其显示为"null".只有当您具有诸如#{bean.entity.subentity.subsubentity.property}这样的嵌套属性时,这才使调试和修复变得更加困难.

    This has actually the same cause as #2, only the (older) EL implementation being used is somewhat buggy in preserving the property name to display in the exception message, which ultimately incorrectly exposed as 'null'. This only makes debugging and fixing a bit harder when you've quite some nested properties like so #{bean.entity.subentity.subsubentity.property}.

    解决方案仍然相同:确保所有级别的嵌套实体都不是null.

    The solution is still the same: make sure that the nested entity in question is not null, in all levels.

    这也与#2具有相同的原因,只有正在使用的(较旧的)EL实现在制定异常消息时存在错误.仅当您在EL中像#{bean.collection[index]}中那样使用大括号符号[]时,才会显示此内容,其中#{bean.collection}本身非空,但指定索引处的项目不存在.然后,必须将此类消息解释为:

    This has also the same cause as #2, only the (older) EL implementation being used is buggy in formulating the exception message. This exposes only when you use the brace notation [] in EL as in #{bean.collection[index]} where the #{bean.collection} itself is non-null, but the item at the specified index doesn't exist. Such a message must then be interpreted as:

    无法到达目标,"collection [0]"返回null

    Target Unreachable, 'collection[0]' returned null

    解决方案也与#2相同:请确保收集项目可用.

    The solution is also the same as #2: make sure that the collection item is available.

    这实际上与#4具有相同的原因,只是使用的(较旧的)EL实现在保留要显示在异常消息中的迭代索引时有些错误,最终错误地将其公开为'BracketSuffix',这实际上是字符].当您在集合中有多个项目时,这只会使调试和修复工作变得更加困难.

    This has actually the same cause as #4, only the (older) EL implementation being used is somewhat buggy in preserving the iteration index to display in the exception message, which ultimately incorrectly exposed as 'BracketSuffix' which is really the character ]. This only makes debugging and fixing a bit harder when you've multiple items in the collection.

    • javax.el.ELException: Error reading 'foo' on type com.example.Bean
    • javax.el.ELException: Could not find property actionMethod in class com.example.Bean
    • javax.el.PropertyNotFoundException: Property 'foo' not found on type com.example.Bean
    • javax.el.PropertyNotFoundException: Property 'foo' not readable on type java.lang.Boolean
    • javax.el.PropertyNotFoundException: Property not found on type org.hibernate.collection.internal.PersistentSet
    • Outcommented Facelets code still invokes EL expressions like #{bean.action()} and causes javax.el.PropertyNotFoundException on #{bean.action}

    这篇关于识别和解决javax.el.PropertyNotFoundException:目标不可访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆