扩展PrimeFaces p:fileUpload时抛出PropertyNotFoundException求助
问题现象
扩展PrimeFaces的p:fileUpload组件后,使用自定义组件的listener属性会抛出PropertyNotFoundException,提示CDI代理类不存在upload属性。但直接使用原生p:fileUpload组件时,#{fileUploadBean.upload}能正常调用对应方法。尝试使用#{fileUploadBean.upload(event)}时,方法虽能执行但event参数为null。
环境配置
- Jakarta Faces 4.0
- Java 17
- PrimeFaces 12/13(两个版本均测试)
- Tomcat 10 或 Quarkus 3.6.5
- CDI环境(beans.xml仅包含
<beans/>,web.xml、faces-config.xml等配置文件为空)
异常信息
Caused by: jakarta.el.PropertyNotFoundException: The class 'org.test.component.FileUploadBean_ClientProxy' does not have the property 'upload'. at jakarta.el.BeanELResolver.getBeanProperty(BeanELResolver.java:626) at jakarta.el.BeanELResolver.getValue(BeanELResolver.java:338) at jakarta.el.CompositeELResolver.getValue(CompositeELResolver.java:136) at org.apache.el.parser.AstValue.getValue(AstValue.java:169) at org.apache.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:190) at org.apache.myfaces.view.facelets.el.ContextAwareTagValueExpression.getValue(ContextAwareTagValueExpression.java:100) ... 50 more
该异常在Quarkus 3.6.5 + PrimeFaces 13.0.3 + MyFaces 4.0.1,以及Tomcat 10 + PrimeFaces 13.0.3 + Jakarta Faces 4.0.5 + Weld Servlet 4.0.0.Final环境中均会出现。
代码示例
自定义FileUpload组件
@FacesComponent(value = "org.test.component.FileUpload", tagName = "fileUpload", namespace = "mycomponents", createTag = true) public class FileUpload extends org.primefaces.component.fileupload.FileUpload {}
CDI处理Bean
@Named @RequestScoped public class FileUploadBean implements Serializable { public void upload(FileUploadEvent event) { System.out.println("Upload: " + event.getFile().getFileName()); } }
测试页面
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:faces="jakarta.faces" xmlns:ui="jakarta.faces.facelets" xmlns:f="jakarta.faces.core" xmlns:h="jakarta.faces.html" xmlns:p="http://primefaces.org/ui" xmlns:w="mycomponents" > <h:head> <meta charset="UTF-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>Test</title> </h:head> <h:body> <h:form enctype="multipart/form-data"> <!-- 抛出异常 --> <w:fileUpload listener="#{fileUploadBean.upload}" auto="true"/> <!-- 正常工作 --> <p:fileUpload listener="#{fileUploadBean.upload}" auto="true"/> </h:form> </h:body> </html>
问题原因
PrimeFaces原生FileUpload组件的listener属性被定义为方法表达式(MethodExpression),但通过@FacesComponent的createTag=true自动生成的标签,会默认将所有属性解析为值表达式(ValueExpression)。这导致EL引擎尝试将upload解析为Bean的属性而非方法,因此抛出PropertyNotFoundException。
解决方案
步骤1:手动创建自定义标签库
放弃createTag=true自动生成标签,手动创建标签库文件/WEB-INF/mycomponents.taglib.xml,明确将listener属性定义为方法表达式类型:
<?xml version="1.0" encoding="UTF-8"?> <facelet-taglib version="4.0" xmlns="jakarta.faces.webapp"> <namespace>mycomponents</namespace> <tag> <tag-name>fileUpload</tag-name> <component> <component-type>org.test.component.FileUpload</component-type> </component> <attribute> <name>listener</name> <method-signature>void upload(org.primefaces.event.FileUploadEvent)</method-signature> </attribute> <!-- 若需要其他属性,可参考PrimeFaces官方标签库定义添加 --> </tag> </facelet-taglib>
步骤2:修改自定义组件类
移除createTag=true及相关自动标签生成的属性,仅保留组件类型定义:
@FacesComponent(value = "org.test.component.FileUpload") public class FileUpload extends org.primefaces.component.fileupload.FileUpload {}
步骤3:注册标签库
在web.xml中添加标签库注册配置:
<context-param> <param-name>jakarta.faces.FACELETS_LIBRARIES</param-name> <param-value>/WEB-INF/mycomponents.taglib.xml</param-value> </context-param>
完成以上配置后,自定义组件的listener属性会被正确解析为方法表达式,既可以正常调用upload方法,也能正确传递FileUploadEvent参数,和原生组件表现一致。
内容的提问来源于stack exchange,提问作者devod

