升级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场景,能避免视图状态莫名其妙丢失的问题 - 要是你的
userbean之前是@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
相关产品推荐
相关产品推荐

