使用omnifaces.ListConverter遇NotSerializableException问题求助(非MyFaces环境)
问题复盘
你遇到的这个java.io.NotSerializableException: org.omnifaces.taghandler.Converter异常,场景很明确:在<p:dataTable>行编辑模式下,搭配使用OmniFaces的ListConverter和<p:autoComplete>时触发,而且BalusC针对MyFaces的方案不适用——毕竟你的项目用的是WebLogic默认的Mojarra实现。
先把典型的代码示例贴出来(方便后续分析):
常见代码场景
XHTML 片段
<p:dataTable value="#{entityBean.items}" var="item" editable="true"> <p:column headerText="关联实体"> <p:cellEditor> <f:facet name="output">#{item.relatedEntity.displayName}</f:facet> <f:facet name="input"> <p:autoComplete value="#{item.relatedEntity}" completeMethod="#{entityBean.completeEntities}" converter="omnifaces.ListConverter" var="entity" itemLabel="#{entity.displayName}" itemValue="#{entity}"> <f:attribute name="list" value="#{entityBean.allEntities}" /> </p:autoComplete> </f:facet> </p:cellEditor> </p:column> <p:column> <p:rowEditor /> </p:column> </p:dataTable>
Managed Bean 片段
@ManagedBean @ViewScoped public class EntityBean implements Serializable { private List<Item> items; private List<RelatedEntity> allEntities; // 初始化、getter/setter省略 public List<RelatedEntity> completeEntities(String query) { return allEntities.stream() .filter(e -> e.getDisplayName().toLowerCase().contains(query.toLowerCase())) .collect(Collectors.toList()); } } // 实体类示例 public class Item implements Serializable { private RelatedEntity relatedEntity; // getter/setter } public class RelatedEntity implements Serializable { private Long id; private String displayName; // getter/setter、equals/hashCode重写 }
异常根源分析
WebLogic 12.2.1的Mojarra实现,在处理视图状态序列化时,会把行编辑模式下的组件状态(包括绑定的Converter)存入会话。而OmniFaces的ListConverter内部依赖的org.omnifaces.taghandler.Converter类没有实现Serializable接口,当视图状态被序列化时,这个非序列化的依赖就会触发异常——这和MyFaces的bug场景不同,所以BalusC的MyFaces方案不适用。
针对性解决方案
方案1:自定义可序列化的ListConverter
最稳妥的方式是自己实现一个逻辑和OmniFaces ListConverter一致,但完全支持序列化的Converter:
@FacesConverter("serializableListConverter") public class SerializableListConverter implements Converter, Serializable { @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { if (value == null || value.isBlank()) { return null; } @SuppressWarnings("unchecked") List<Object> targetList = (List<Object>) component.getAttributes().get("list"); if (targetList == null) { throw new IllegalArgumentException("组件未绑定list属性,请检查<f:attribute name='list'/>配置"); } // 匹配逻辑:用toString()对应(和OmniFaces ListConverter一致) return targetList.stream() .filter(item -> value.equals(item.toString())) .findFirst() .orElse(null); } @Override public String getAsString(FacesContext context, UIComponent component, Object value) { return (value == null) ? "" : value.toString(); } }
然后把xhtml里的converter="omnifaces.ListConverter"替换成converter="serializableListConverter"即可,其他配置保持不变。
方案2:调整WebLogic序列化过滤规则
如果不想自定义Converter,可以尝试让WebLogic忽略这个非序列化类的检查:
- 打开WebLogic控制台,找到你的应用部署项
- 进入「配置」->「常规」,找到「序列化过滤」配置
- 添加
org.omnifaces.taghandler.Converter到「允许的非序列化类」列表 - 或者在项目的
weblogic.xml中添加如下配置:
<wls:application-param> <wls:param-name>weblogic.servlet.session.enableSerializationFilter</wls:param-name> <wls:param-value>false</wls:param-value> </wls:application-param>
⚠️ 注意:关闭序列化过滤可能带来其他潜在的序列化问题,建议在测试环境充分验证后再上线。
方案3:升级OmniFaces版本
检查你当前使用的OmniFaces版本,如果是较旧的版本(比如2.x早期版本),可以尝试升级到兼容JSF 2.2的稳定版本(比如OmniFaces 3.14,它支持JSF 2.2+)——新版本可能已经修复了这个序列化相关的bug,让ListConverter不再依赖非序列化的taghandler类。
方案4:绕过Converter,直接绑定实体
如果场景允许,也可以尝试不用ListConverter:在<p:autoComplete>的itemValue直接绑定实体,同时确保你的RelatedEntity正确重写了equals()和hashCode()方法,PrimeFaces会自动处理实体的匹配逻辑,这样就避开了Converter序列化的问题。
内容的提问来源于stack exchange,提问作者VaazhgaV

