You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Liferay 6.2至7:如何用JSF selectOneListbox存储Portlet偏好

我刚帮几个朋友搞定过Liferay 6.2到7的JSF Portlet升级,看你这段selectOneListbox的代码,正好踩过几个坑,整理了关键调整点给你:

针对Liferay 7中JSF Portlet selectOneListbox 升级的核心调整

1. 先把Managed Bean换成CDI风格的

Liferay 7对JSF的bean管理更偏向CDI,如果你之前用的是@ManagedBean,赶紧换成@Named,作用域也得调整:

  • 别再用JSF原生的@ViewScoped了,改用Liferay专属的@PortletViewScoped,这玩意儿专门适配Portlet场景,能避免视图状态莫名其妙丢失的问题
  • 要是你的user bean之前是@SessionScoped,升级后得注意Liferay 7的会话管理更严格,能不用会话作用域就尽量不用,减少不必要的资源占用

2. AJAX渲染的组件ID别踩坑

你代码里写的render=":metricsPanel"在Liferay 7里可能出问题,因为容器会自动给Portlet组件加命名空间:

  • 要是metricsPanel就在当前Portlet的表单里,直接写render="metricsPanel"就行,不用加冒号(冒号是跨命名空间的用法,一般不用在Portlet内部)
  • 实在拿不准的话,可以动态获取组件的客户端ID,比如在bean里注入FacesContext,用facesContext.getViewRoot().findComponent("metricsPanel").getClientId()拿到正确的ID再传给AJAX

3. selectItems的数据绑定要严谨

Liferay 7的EL表达式解析更严格,你的listModes得确保没问题:

  • 如果是自定义的POJO,一定要给label和value属性写好getter方法,EL表达式才读得到
  • 别在getListModes()方法里做初始化操作,Liferay 7的JSF生命周期会多次调用getter,把数据初始化放在@PostConstruct注解的方法里才靠谱

4. setListMode方法的调试技巧

当调用user.setListMode时如果出现值不更新或者报错,按这几步排查:

  • 先给setListMode加个日志,比如用Liferay的Log类打个info级别的日志,看看有没有被调用,传入的值对不对
  • 要是listMode是自定义类型,得检查有没有对应的@FacesConverter,或者直接在selectOneListbox上指定converter属性,不然JSF没法把前端的字符串转成你要的对象
  • 去看Liferay的日志文件(tomcat/logs/liferay.*.log),里面的JSF生命周期异常信息比前端提示详细多了,大部分问题都能在这儿找到原因

5. 部署依赖要调整成OSGi风格

Liferay 7用OSGi模块部署,你的JSF Portlet得调整依赖配置:

  • 确保bnd.bnd文件里正确导入了JSF和Liferay Faces相关的包,比如javax.faces, com.liferay.faces.portal
  • 用对应版本的Liferay Faces Bridge,比如Liferay 7.4就用5.x版本的Bridge,别乱混用版本
  • 别把JSF的jar包打包到Portlet里,尽量用容器提供的依赖,不然容易出现类冲突

给你个调整后的代码示例,用CDI和@PortletViewScoped:

import com.liferay.faces.util.context.FacesContextHelper;
import javax.enterprise.context.PortletViewScoped;
import javax.inject.Named;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.model.SelectItem;

@Named("user")
@PortletViewScoped
public class UserBean implements Serializable {

    private String listMode;
    private List<SelectItem> listModes;

    @PostConstruct
    public void init() {
        // 初始化列表选项,放在PostConstruct里只执行一次
        listModes = new ArrayList<>();
        listModes.add(new SelectItem("grid", "网格视图"));
        listModes.add(new SelectItem("list", "列表视图"));
    }

    public String getListMode() {
        return listMode;
    }

    public void setListMode(String listMode) {
        // 加日志调试,看看方法有没有被调用
        FacesContextHelper.getLogger(UserBean.class).info("切换到视图模式:" + listMode);
        this.listMode = listMode;
    }

    public List<SelectItem> getListModes() {
        return listModes;
    }
}

对应的xhtml代码调整(去掉了冒号,确保组件在同一命名空间):

<h:selectOneListbox id="listModeSelector" value="#{user.listMode}" size="1">
    <f:selectItems value="#{user.listModes}" var="mode" itemLabel="#{mode.label}" itemValue="#{mode.value}" />
    <f:ajax event="change" execute="@this" render="metricsPanel" />
</h:selectOneListbox>

<!-- 确保metricsPanel和selectOneListbox在同一NamingContainer下(比如同一表单里) -->
<h:panelGroup id="metricsPanel">
    <!-- 你的图标列表内容放在这儿 -->
</h:panelGroup>

要是还是有问题,先排查EL表达式能不能正常解析,比如在页面上直接输出#{user.listMode}看看值对不对,然后检查Portlet的视图状态有没有被正确维护,@PortletViewScoped是Liferay 7里JSF Portlet最稳定的作用域,能解决大部分状态丢失的问题。

内容的提问来源于stack exchange,提问作者Sechanris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:30:26