识别并解决javax.el.PropertyNotFoundException:Target Unreachable

Bal*_*usC 117 jsf el cdi managed-bean propertynotfoundexception

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

似乎有五种不同的消息:

  1. 目标无法访问,标识符'bean'已解析为null
  2. 目标无法访问,'entity'返回null
  3. 目标无法访问,'null'返回null
  4. 目标无法访问,''0'返回null
  5. 目标无法访问,'BracketSuffix'返回null

这些都意味着什么?它们是如何引起的,它们应该如何解决?

Bal*_*usC 225

1.目标无法访问,标识符'bean'已解析为null

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

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

一个.谁在管理豆子?
湾 什么是(默认)托管bean名称?
C.支持bean类在哪里?

1A.谁在管理豆子?

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谁管理bean @ManagedBean,那么你需要确保以下内容:

  • 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">
    
    Run Code Online (Sandbox Code Playgroud)

    对于JSF 2.1,只需替换2_02.0by 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">
    
    Run Code Online (Sandbox Code Playgroud)

    对于JSF 2.3,只需更换2_22.2通过2_32.3分别.

  • 你没有意外导入javax.annotation.ManagedBean而不是javax.faces.bean.ManagedBean.注意IDE自动完成,已知Eclipse会自动提取错误的列表作为列表中的第一项.

  • 您没有在同一个支持bean类中覆盖@ManagedBeanJSF 1.x样式<managed-bean>条目faces-config.xml以及不同的托管bean名称.这个优先于@ManagedBean.faces-config.xml从JSF 2.0开始,无需注册托管bean ,只需将其删除即可.
  • Your runtime classpath is clean and free of duplicates in JSF API related JARs. Make sure that you're not mixing multiple JSF implementations (Mojarra and MyFaces). Make sure that you don't provide another JSF or even Java EE API JAR file along webapp when the target container already bundles JSF API out the box. See also "Installing JSF" section of our JSF wiki page for JSF installation instructions. In case you intend to upgrade container-bundled JSF from the WAR on instead of in container itself, make sure that you've instructed the target container to use WAR-bundled JSF API/impl.
  • If you're packaging JSF managed beans in a JAR, then make sure that the JAR has at least a JSF 2.0 compatible /META-INF/faces-config.xml. See also How to reference JSF managed beans which are provided in a JAR file?
  • 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).


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) 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>
    
    Run Code Online (Sandbox Code Playgroud)
  • 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>
    
    Run Code Online (Sandbox Code Playgroud)
  • 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.

  • When using Mojarra 2.3.0-2.3.2 and CDI 1.1+ with bean-discovery-mode="annotated" (default), then you need to upgrade Mojarra to 2.3.3 or newer due to a bug. In case you can't upgrade, then you need either to set bean-discovery-mode="all" in beans.xml, or to put the JSF 2.3 specific @FacesConfig annotation on an arbitrary class in the WAR (generally some sort of an application scoped startup class).
  • Non-Java EE containers like Tomcat and Jetty doesn't ship with CDI bundled. You need to install it manually. It's a bit more work than just adding the library JAR(s). For Tomcat, make sure that you follow the instructions in this answer: How to install and use CDI on Tomcat?
  • Your runtime classpath is clean and free of duplicates in CDI API related JARs. Make sure that you're not mixing multiple CDI implementations (Weld, OpenWebBeans, etc). Make sure that you don't provide another CDI or even Java EE API JAR file along webapp when the target container already bundles CDI API out the box.
  • 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).


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>
    
    Run Code Online (Sandbox Code Playgroud)

    And this in faces-config.xml:

    <application>
        <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
    </application>
    
    Run Code Online (Sandbox Code Playgroud)
  • (above is all I know with regard to Spring — I don't do Spring — feel free to edit/comment with other probable Spring related causes; e.g. some XML configuration related trouble)


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?


1b. What's the (default) managed bean name?

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.

  • A FooBean backing bean class like below,

    @Named
    public class FooBean {}
    
    Run Code Online (Sandbox Code Playgroud)

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

  • A FOOBean backing bean class like below,

    @Named
    public class FOOBean {}
    
    Run Code Online (Sandbox Code Playgroud)

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

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

    @Named("foo")
    public class FooBean {}
    
    Run Code Online (Sandbox Code Playgroud)

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


1c. Where's the backing bean class?

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.

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.

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


2. Target Unreachable, 'entity' returned 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}" />
Run Code Online (Sandbox Code Playgroud)

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();
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

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.

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?

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?


3. Target Unreachable, 'null' returned null

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

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


4. Target Unreachable, ''0'' returned null

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:

Target Unreachable, 'collection[0]' returned null

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


5. Target Unreachable, 'BracketSuffix' returned null

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.


Other possible causes of javax.el.PropertyNotFoundException:


sei*_*cle 6

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

将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>
Run Code Online (Sandbox Code Playgroud)

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


归档时间:

查看次数:

107351 次

最近记录:

5 年,9 月 前