自2016年6月Richfaces已停止更新,最近将一项目迁移到了Primefaces。Primefaces提供了更丰富的组件,使用更简单,界面更美观,性能更高。JSF应用现在越来越少,仅供有需要者参考。

Richfaces版本: 4.5.14.Final
Primefaces版本: 6.2

第一步 删除Richfaces配置,引入Primefaces

如果您使用了maven,请从pom中删除richfaces配置,然后加入primefaces:

<dependency> <groupId>org.primefaces</groupId> <artifactId>primefaces</artifactId> <version>6.2</version></dependency>

然后将web.xml中的richfaces配置删除。

批量更新

xmlns:rich="http://richfaces.org/rich"xmlns:a4j="http://richfaces.org/a4j"

替换为:

xmlns:p="http://primefaces.org/ui"

注意xmlns:p="http://primefaces.org/ui" 不能重复。

将 rich: 和 a4j: 替换为 p:。将属性render替换为update,execute替换为process。删除属性limitRender、bypassUpdates。同名组件RichfacesPrimefaces<a4j:ajax><p:ajax><a4j:commandButton><p:commandButton><a4j:commandLink><p:commandLink><a4j:log><p:log><a4j:outputPanel><p:outputPanel><a4j:poll><p:poll><a4j:repeat><p:repeat><rich:calendar><p:calendar><rich:contextMenu><p:contextMenu><rich:dataGrid><p:dataGrid><rich:dataTable> <rich:dataScroller><p:dataTable><rich:extendedDataTable> <rich:collapsibleSubTable><p:dataTable><rich:editor><p:editor><rich:fileUpload><p:fileUpload><rich:message><p:message><rich:messages><p:messages><rich:panel><p:panel><rich:panelMenu><p:panelMenu><rich:pickList><p:pickList><rich:progressBar><p:progressBar><rich:toolbar><p:toolbar><rich:tooltip><p:tooltip><rich:tree><p:tree>异名组件RichfacesPrimefaces<a4j:jsFunction><p:remoteCommand><a4j:mediaOutput><p:media><a4j:region><p:fragment><a4j:status><p:ajaxStatus><rich:validator><p:clientValidator><rich:accordion> <rich:collapsiblePanel><p:accordionPanel><rich:autocomplete><p:autoComplete><rich:chart>Primefaces提供了十多种chart组件,自行选择<rich:dragSource><p:draggable><rich:dropTarget><p:droppable><rich:dropDownMenu><p:menubar><rich:list><p:dataList><rich:inplaceInput> <rich:inplaceSelect><p:inplace><rich:inputNumberSlider><p:slider><rich:inputNumberSpinner><p:spinner><rich:notify><p:notificationBar><rich:notifyMessage> <rich:notifyMessages><p:growl><rich:orderingList><p:orderList><rich:popupPanel><p:dialog><rich:select><p:selectOneMenu><rich:tabPanel> <rich:togglePanel><p:tabView>不存在的组件

<a4j:param>
<a4j:actionListener>
<a4j:push>
<a4j:queue>

说明:<rich:dataScroller>与<p:dataScroller>,虽然名称相同,但功能不同,一个是分页,一个是按需显示数据。

子组件、属性、事件

名称或功能相同的组件,子组件名称,支持的属性、事件可能不同,使用时需要仔细核对,如:

<rich:panelMenu> -> <p:panelMenu>
<p:panelMenu> 仅支持几个常用属性
<rich:panelMenuGroup> -> <p:submenu>
<rich:panelMenuItem> -> <p:menuitem>
label -> value<rich:collapsiblePanel> -> <p:accordionPanel>
要删除原定义的属性,增加子组件<p:tab>Function#{rich:component('id')} -> PF('widgetVar')

<a4j:commandButton value="Cancel" oncomplete="#{rich:component('popupPanel')}.hide();"/><rich:popupPanel id="popupPanel">...</rich:popupPanel>

<a4j:commandButton value="Cancel" oncomplete="PF('dialog').hide()"/><p:dialog id="dialog" widgetVar="dialog">...</p:dialog>#{rich:clientId('id')} -> #{p:component('id')}jQuery
在Primefaces中可以直接使用jQuery,如:

<p:commandButton onclick="alert($('#form\\:panel\\:airport').val());return false;" value="test"/>

上面使用了转义字符,也可以使用PrimeFaces函数,注意不要加"#",如下:

<p:commandButton onclick="alert($(PrimeFaces.escapeClientId('form:panel:airport')).val());return false;" value="test"/>Primefaces特有组件

Primefaces提供了很多新颖美观的组件,也可以替代Richfaces组件,大家可查看Primefaces showcase。
有一些比较常用的功能Richfaces没有提供,原来只能自己实现,现在不需要了,比如:

<p:outputLabel>
自动为required增加*,为校验信息增加label

<p:outputLabel for="extended" value="Extended Label:" /><p:inputText id="extended" required="true" /><p:panelGrid>
支持复杂表格,使用Richfaces时只能借助<rich:dataTable>较繁琐。<p:confirmDialog>
confirmDialog示例:

<p:commandButton value="Destroy the World" actionListener="#{dialogView.destroyWorld}" update="message"><p:confirm header="Confirmation" message="Are you sure?" icon="ui-icon-alert" /></p:commandButton><p:confirmDialog global="true" showEffect="fade" hideEffect="fade"><p:commandButton value="Yes" type="button" styleClass="ui-confirmdialog-yes" icon="ui-icon-check" /><p:commandButton value="No" type="button" styleClass="ui-confirmdialog-no" icon="ui-icon-close" /></p:confirmDialog>验证

<h:inputText value="" required="true" id="test"> <p:ajax event="blur" update="msgTest"/></h:inputText><p:message for="test" id="msgTest" display="icon"/>

<rich:message>不支持tooltip形式显示message,<p:message>支持。

dataTable与LazyDataModel

<p:dataTable>实现了<rich:dataTable>、<rich:extendedDataTable>、<rich:dataScroller>的所有功能,定义更简单,且复杂表头时仍支持排序,支持单选、复选框选择,提供emptyMessage属性,不必再使用noData facet。
LazyDataModel比ExtendedDataModel实现更简单,性能更高。
下面LazyDataModel和自定义dataTable示例(org.jason是自定义的一些实现):
PagingDataModel

import org.jason.base.dao.Filter;import org.jason.base.dao.IQuery;import org.jason.base.dao.Order;import org.jason.base.dao.Pagination;import org.jason.base.service.PagingService;import org.jason.entity.BaseEntity;import org.primefaces.model.LazyDataModel;import org.primefaces.model.SortOrder;import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Map;import java.util.stream.Collectors;import static org.primefaces.model.SortOrder.ASCENDING;//public class PagingDataModel<T extends BaseEntity> extends LazyDataModel<T> { private PagingService service; private IQuery query; private List<T> data; private List<T> selectedItems = new ArrayList<>(); private T selectedItem; @SuppressWarnings("rawtypes") public PagingDataModel(PagingService service, IQuery query) { this.service = service; this.query = query; } @Override public T getRowData(String rowKey) { for (T entity : data) { if (entity.getId() == Integer.parseInt(rowKey)) { return entity; } } return null; } @Override public Object getRowKey(T object) { return object.getId(); } @Override public List<T> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) { data = service.find(query, new Pagination(first, pageSize, createFilter(filters), createOrders(sortField, sortOrder))); setRowCount(service.count(query, createFilter(filters))); return data; } public void refresh() { setSelectedItem(null); setSelectedItems(null); load(0, getPageSize(), null, null, null); } private List<Filter> createFilter(Map<String, Object> filters) { if (filters == null) { return Collections.emptyList(); } return filters.entrySet().stream().map(filter -> new Filter(filter.getKey(), filter.getValue())).collect(Collectors.toList()); } private List<Order> createOrders(String sortField, SortOrder sortOrder) { if (sortField == null || sortOrder == null) { return Collections.emptyList(); } List<Order> orders = new ArrayList<>(); orders.add(new Order(sortField, sortOrder == ASCENDING)); return orders; } public List<T> getSelectedItems() { return selectedItems; } public void setSelectedItems(List<T> selectedItems) { this.selectedItems = selectedItems; } public T getSelectedItem() { return selectedItem; } public void setSelectedItem(T selectedItem) { this.selectedItem = selectedItem; }}

自定义dataTable

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:composite="http://java.sun.com/jsf/composite" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" xmlns:c="http://java.sun.com/jsp/jstl/core"><composite:interface> <composite:attribute name="emptyMessage" default="#{messages['general.noData']}"/> <composite:attribute name="paginator" default="true"/> <composite:attribute name="paginatorPosition" default="bottom"/> <composite:attribute name="rows" default="10"/> <composite:attribute name="rowsPerPageTemplate" default="5,10,15"/> <composite:attribute name="rowStyleClass" default="even,odd"/> <composite:attribute name="selection"/> <composite:attribute name="selectionMode"/> <composite:attribute name="style"/> <composite:attribute name="styleClass" default="list-table"/> <composite:attribute name="value" type="org.iata.ios.web.pagination.PagingDataModel" required="true"/> <composite:attribute name="onRowClick"/> <composite:facet name="header"/> <composite:facet name="footer"/></composite:interface><composite:implementation> <p:outputPanel layout="block" styleClass="list-panel"> <c:choose> <c:when test="#{not empty cc.attrs.selectionMode}"> <p:dataTable id="dt" value="#{cc.attrs.value}" var="item" lazy="true" rows="#{cc.attrs.paginator ? cc.attrs.rows : 0}" paginator="#{cc.attrs.paginator}" paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}" rowsPerPageTemplate="#{cc.attrs.rowsPerPageTemplate}" paginatorPosition="#{cc.attrs.paginatorPosition}" selection="#{cc.attrs.selection}" emptyMessage="#{cc.attrs.emptyMessage}" styleClass="#{cc.attrs.styleClass}" rowStyleClass="#{cc.attrs.rowClasses}" onRowClick="#{cc.attrs.onRowClick}"> <composite:insertFacet name="header"/> <p:column rendered="#{cc.attrs.selectionMode == 'single'}" selectionMode="single" /> <p:column rendered="#{cc.attrs.selectionMode == 'multiple'}" selectionMode="multiple" /> <composite:insertChildren/> <composite:insertFacet name="footer"/> </p:dataTable> </c:when> <c:otherwise> <p:dataTable id="dt" value="#{cc.attrs.value}" var="item" lazy="true" rows="#{cc.attrs.paginator ? cc.attrs.rows : 0}" paginator="#{cc.attrs.paginator}" paginatorTemplate="{CurrentPageReport} {FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}" rowsPerPageTemplate="#{cc.attrs.rowsPerPageTemplate}" paginatorPosition="#{cc.attrs.paginatorPosition}" emptyMessage="#{cc.attrs.emptyMessage}" styleClass="#{cc.attrs.styleClass}" rowStyleClass="#{cc.attrs.rowClasses}" onRowClick="#{cc.attrs.onRowClick}"> <composite:insertFacet name="header"/> <composite:insertChildren/> <composite:insertFacet name="footer"/> </p:dataTable> </c:otherwise> </c:choose> </p:outputPanel> <ui:include src="css.xhtml"/></composite:implementation></html>参考文档

PrimeFaces Showcase
PrimeFaces Documentation
Migrating From RichFaces 4 to PrimeFaces 3
Patching RichFaces 3.3.3 AJAX.js for IE11

附录 Seam升级到CDI替换Annotation@Name -> @Named
import org.jboss.seam.annotations.Name; -> import javax.inject.Named;@In -> @Inject
import org.jboss.seam.annotations.In; -> import javax.inject.Inject;删除@Out
import org.jboss.seam.annotations.Out;
改为@Inject相应Scope的组件@Scope(ScopeType.APPLICATION) -> @ApplicationScoped
@Scope(ScopeType.SESSION) -> @SessionScoped
@Scope(ScopeType.CONVERSATION) -> @ConversationScoped
@Scope(ScopeType.EVENT) -> @RequestScoped(或删除)@Create -> @PostConstruct
import org.jboss.seam.annotations.Create; -> import javax.annotation.PostConstruct;
@Destroy -> @PreDestroy
import org.jboss.seam.annotations.Destroy; -> import javax.annotation.PreDestroy;@Observer("org.jboss.seam.postInitialization")
可以使用DeltaSpike Servlet module,替换如下:

public void init(@Observes @Initialized ServletContext context) {...}

引入DeltaSpike Servlet module:

<dependency><groupId>org.apache.deltaspike.modules</groupId><artifactId>deltaspike-servlet-module-api</artifactId><version>1.8.1</version></dependency><dependency><groupId>org.apache.deltaspike.modules</groupId><artifactId>deltaspike-servlet-module-impl</artifactId><version>1.8.1</version></dependency>

在web.xml中增加:

<listener><display-name>EventBridgeContextListener</display-name><listener-class>org.apache.deltaspike.servlet.impl.event.EventBridgeContextListener</listener-class></listener>@Startup可使用ejb Singleton替换:

@Singleton@Startup@RequestParameter和页面参数
可以替换为这样的组合:

<p:button value="Edit" outcome="edit"><f:param name="id" value="#{...}"/></p:button>

<f:metadata><f:viewParam name="id" value="#{...}"/></f:metadata>@Logger 建议替换为slf4j

@Loggerprivate Log log;

替换为:
private Logger log = LoggerFactory.getLogger(Test.class);
日志中的{0} {1} 均替换为{}。

FacesMessages和StatusMessages改为使用javax.faces.application.FacesMessage更新配置删除components.xmlseam.properties替换为beans.xml,内容如下:

<?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>删除page.xml
navigation调整到JSF 2 faces-config.xml中(navigation-rule)。
action可以调整到页面中:

<f:metadata><f:event type="preRenderView" listener="#{...}"/></f:metadata>

页面参数上面已提到过。

页面

xmlns:s="http://jboss.com/products/seam/taglib", 可以使用JSF 2或Primefaces标签替换

SeamJSF 2/Primefaces<s:div><p:outputPanel><s:span><p:outputPanel layout="inline"><s:enumItem><f:selectItem> label -> itemLabel enumValue -> itemValue<s:convertDateTime><f:convertDateTime><s:fragment><ui:fragment><s:conversationId><f:param name="cid" value="#{javax.enterprise.context.conversation.id}"/>参考文档

seam migration
Migration from Seam 2 to Java EE and Alternatives