
如果計費部門要開發一個用于創建發票的新對話框,則可能如下所示:

如您所見,上面的屏幕在上部引用了一個客戶。 單擊短名稱文本字段后面的“ ..”按鈕將打開以下對話框,允許用戶選擇客戶:

按“選擇”后,客戶數據將顯示在發票表格中。
也可以通過簡單地輸入客戶編號或在發票屏幕上的文本字段中輸入簡稱來選擇客戶。 如果輸入唯一的短名稱,則根本不會出現選擇對話框。 而是直接顯示客戶數據。 只有不明確的簡稱會導致打開客戶選擇屏幕。
客戶功能將由屬于客戶管理團隊的開發人員提供。 一種典型的方法是由客戶管理開發團隊提供一些服務,而計費部門的開發人員創建用戶界面并調用這些服務。
但是,這種方法涉及到這兩個不同部門之間的耦合要強于實際需要。 發票只需要一個唯一的ID即可引用客戶數據。 創建發票對話框的開發人員實際上并不想知道如何查詢客戶數據或在后臺使用哪些服務來獲取該信息。
客戶管理開發人員應提供UI的完整部分,以顯示客戶ID并處理客戶的選擇:

使用JSF 2,使用復合組件很容易實現。 客戶管理部門和計費部門之間的邏輯接口包括三個部分:
- 復合組件(XHTML)
- 復合組件的支持bean
- 偵聽器界面,用于處理選擇結果
提供者(客戶管理部門)
復合組件:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"xmlns:ui="http://java.sun.com/jsf/facelets"xmlns:h="http://java.sun.com/jsf/html"xmlns:f="http://java.sun.com/jsf/core"xmlns:composite="http://java.sun.com/jsf/composite"xmlns:ice="http://www.icesoft.com/icefaces/component"xmlns:ace="http://www.icefaces.org/icefaces/components"xmlns:icecore="http://www.icefaces.org/icefaces/core"><ui:composition><composite:interface name="customerSelectionPanel" displayName="Customer Selection Panel" shortDescription="Select a customer using it's number or short name"><composite:attribute name="model" type="org.fuin.examples.soui.view.CustomerSelectionBean" required="true" /> </composite:interface><composite:implementation><ui:param name="model" value="#{cc.attrs.model}"/><ice:form id="customerSelectionForm"><icecore:singleSubmit submitOnBlur="true" /><h:panelGroup id="table" layout="block"><table><tr><td><h:outputLabel for="customerNumber"value="#{messages.customerNumber}" /></td><td><h:inputText id="customerNumber"value="#{model.id}" required="false" /></td><td> </td><td><h:outputLabel for="customerShortName"value="#{messages.customerShortName}" /></td><td><h:inputText id="customerShortName"value="#{model.shortName}" required="false" /></td><td><h:commandButton action="#{model.select}"value="#{messages.select}" /></td></tr><tr><td><h:outputLabel for="customerName"value="#{messages.customerName}" /></td><td colspan="5"><h:inputText id="customerName"value="#{model.name}" readonly="true" /></td></tr></table></h:panelGroup></ice:form></composite:implementation></ui:composition></html>
復合組件的后備bean:
package org.fuin.examples.soui.view;import java.io.Serializable;import javax.enterprise.context.Dependent;
import javax.inject.Inject;
import javax.inject.Named;import org.apache.commons.lang.ObjectUtils;
import org.fuin.examples.soui.model.Customer;
import org.fuin.examples.soui.services.CustomerService;
import org.fuin.examples.soui.services.CustomerShortNameNotUniqueException;
import org.fuin.examples.soui.services.UnknownCustomerException;@Named
@Dependent
public class CustomerSelectionBean implements Serializable {private static final long serialVersionUID = 1L;private Long id;private String shortName;private String name;private CustomerSelectionListener listener;@Injectprivate CustomerService service;public CustomerSelectionBean() {super();listener = new DefaultCustomerSelectionListener();}public Long getId() {return id;}public void setId(final Long id) {if (ObjectUtils.equals(this.id, id)) {return;}if (id == null) {clear();} else {clear();this.id = id;try {final Customer customer = service.findById(this.id);changed(customer);} catch (final UnknownCustomerException ex) {FacesUtils.addErrorMessage(ex.getMessage());}}}public String getShortName() {return shortName;}public void setShortName(final String shortNameX) {final String shortName = (shortNameX == "") ? null : shortNameX;if (ObjectUtils.equals(this.shortName, shortName)) {return;}if (shortName == null) {clear();} else {if (this.id != null) {clear();}this.shortName = shortName;try {final Customer customer = service.findByShortName(this.shortName);changed(customer);} catch (final CustomerShortNameNotUniqueException ex) {select();} catch (final UnknownCustomerException ex) {FacesUtils.addErrorMessage(ex.getMessage());}}}public String getName() {return name;}public CustomerSelectionListener getConnector() {return listener;}public void select() {// TODO Implement...}public void clear() {changed(null);}private void changed(final Customer customer) {if (customer == null) {this.id = null;this.shortName = null;this.name = null;listener.customerChanged(null, null);} else {this.id = customer.getId();this.shortName = customer.getShortName();this.name = customer.getName();listener.customerChanged(this.id, this.name);}}public void setListener(final CustomerSelectionListener listener) {if (listener == null) {this.listener = new DefaultCustomerSelectionListener();} else {this.listener = listener;}}public void setCustomerId(final Long id) throws UnknownCustomerException {clear();if (id != null) {clear();this.id = id;changed(service.findById(this.id));}}private static final class DefaultCustomerSelectionListener implementsCustomerSelectionListener {@Overridepublic final void customerChanged(final Long id, final String name) {// Do nothing...}}}
用于處理結果的偵聽器接口:
package org.fuin.examples.soui.view;/*** Gets informed if customer selection changed.*/
public interface CustomerSelectionListener {/*** Customer selection changed.** @param id New unique customer identifier - May be NULL.* @param name New customer name - May be NULL.*/public void customerChanged(Long id, String name);}
用戶(計費部門)
發票Bean只是通過注入來使用客戶選擇Bean,并使用偵聽器接口連接到它:
package org.fuin.examples.soui.view;import java.io.Serializable;import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.inject.New;
import javax.inject.Inject;
import javax.inject.Named;@Named("invoiceBean")
@SessionScoped
public class InvoiceBean implements Serializable {private static final long serialVersionUID = 1L;@Inject @Newprivate CustomerSelectionBean customerSelectionBean;private Long customerId;private String customerName;@PostConstructpublic void init() {customerSelectionBean.setListener(new CustomerSelectionListener() {@Overridepublic final void customerChanged(final Long id, final String name) {customerId = id;customerName = name;}});}public CustomerSelectionBean getCustomerSelectionBean() {return customerSelectionBean;}public String getCustomerName() {return customerName;}}
最后,在發票XHTML中,使用了復合組件并將其鏈接到注入的支持bean:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"xmlns:ui="http://java.sun.com/jsf/facelets"xmlns:h="http://java.sun.com/jsf/html"xmlns:f="http://java.sun.com/jsf/core"xmlns:fuin="http://fuin.org/examples/soui/facelets"xmlns:customer="http://java.sun.com/jsf/composite/customer"><ui:composition template="/WEB-INF/templates/template.xhtml"><ui:param name="title" value="#{messages.invoiceTitle}" /><ui:define name="header"></ui:define><ui:define name="content"><customer:selection-panel model="#{invoiceBean.customerSelectionBean}" /></ui:define><ui:define name="footer"></ui:define></ui:composition></html>
摘要
總之,用戶界面中引用其他部門數據的部分應由提供數據的部門負責。 然后,可以很容易地對提供的代碼進行任何更改,而無需對使用代碼進行任何更改。 此方法的另一個重要好處是可以統一應用程序的用戶界面。 顯示相同數據的控件和面板始終看起來相同。 每個部門還可以為其提供的用戶界面組件創建一個存儲庫,從而使設計新對話框的過程像將正確的組件放在一起一樣容易。
參考: A Java Developer's Life博客上的JCG合作伙伴 Michael Schnell提供的面向服務的UI 。
翻譯自: https://www.javacodegeeks.com/2012/09/service-oriented-ui-with-jsf.html