首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JSF 自定義復合組件開發最佳實踐:重用標準渲染器、狀態管理與事件監聽

     編輯:Java 相關知識

傳統 JSF 組件開發需手寫 encode/decode、saveState/restoreState、實現 NamingContainer/StateHolder 等接口,複雜度極高。本文提出「重用標準組件」策略,通過 ValueScroller(數值增減輸入組件)示例,展示如何零渲染器代碼、零狀態管理代碼完成復合組件開發。

兩大核心原則

  1. 極致重用標準組件功能:容器用 UIPanel,子組件用 UIInput、UICommand、UIGraphic,直接設置標準渲染器類型(javax.faces.Grid、javax.faces.Text、javax.faces.Link、javax.faces.Image),框架自動完成編碼/解碼。
  2. 清晰分離組件類、標簽類、模型類:組件類不依賴 javax.faces.component.html,僅用抽象基類;模型類純 POJO 無 UI 依賴,便於跨框架重用。

ValueScroller 實現全景

組件類

public class ValueScroller extends UIPanel {
    private UIInput input;
    private static final String PANEL_GRID_RENDERER = "javax.faces.Grid";
    private static final String INPUT_TEXT_RENDERER = "javax.faces.Text";
    private static final String COMMAND_LINK_RENDERER = "javax.faces.Link";
    private static final String GRAPHIC_IMAGE_RENDERER = "javax.faces.Image";

    public ValueScroller() { super(); addChildrenAndFaces(); }

    private void addChildrenAndFaces() {
        // 容器屬性
        setRendererType(PANEL_GRID_RENDERER);
        getAttributes().put("columns", 2);

        // 數值輸入框
        input = new UIInput();
        input.setId("input");
        input.setRendererType(INPUT_TEXT_RENDERER);
        getChildren().add(input);

        // 增減按鈕面板
        UIPanel linkPanel = new UIPanel();
        linkPanel.setId("linkPanel");
        linkPanel.setRendererType(PANEL_GRID_RENDERER);
        linkPanel.getAttributes().put("columns", 1);

        ScrollerActionListener listener = new ScrollerActionListener();

        // 增加鏈接 + 圖標
        UICommand upLink = new UICommand();
        upLink.setId("upLink");
        upLink.setRendererType(COMMAND_LINK_RENDERER);
        upLink.addActionListener(listener);
        UIGraphic upImg = new UIGraphic();
        upImg.setId("upImg");
        upImg.setRendererType(GRAPHIC_IMAGE_RENDERER);
        upImg.setUrl("/images/up.gif");
        upLink.getChildren().add(upImg);
        linkPanel.getChildren().add(upLink);

        // 減少鏈接 + 圖標
        UICommand downLink = new UICommand();
        downLink.setId("downLink");
        downLink.setRendererType(COMMAND_LINK_RENDERER);
        downLink.addActionListener(listener);
        UIGraphic downImg = new UIGraphic();
        downImg.setId("downImg");
        downImg.setRendererType(GRAPHIC_IMAGE_RENDERER);
        downImg.setUrl("/images/down.gif");
        downLink.getChildren().add(downImg);
        linkPanel.getChildren().add(downLink);

        getChildren().add(linkPanel);
    }

    // 內部 ActionListener 直接操作 input 值
    private class ScrollerActionListener implements ActionListener {
        public void processAction(ActionEvent e) {
            if (!(input.getValue() instanceof Integer)) throw new IllegalArgumentException("Only Integer supported");
            int val = (Integer) input.getValue();
            String cmdId = ((UICommand) e.getSource()).getId();
            int step = getStep(), min = getMin(), max = getMax();
            if (cmdId.equals("upLink")) {
                input.setValue(val + step > max ? max : val + step);
            } else if (cmdId.equals("downLink")) {
                input.setValue(val - step < min ? min : val - step);
            }
        }
    }
    // min/max/step/value 屬性省略 getter/setter
}

標簽類

public class ValueScrollerTag extends UIComponentTag {
    protected void setProperties(UIComponent component) {
        super.setProperties(component);
        ValueScroller vs = (ValueScroller) component;
        Application app = FacesContext.getCurrentInstance().getApplication();
        // value 必須為 ValueBinding 表達式
        if (value != null) {
            if (isValueReference(value)) vs.setValueBinding("value", app.createValueBinding(value));
            else throw new IllegalArgumentException("value must be a binding expression");
        }
        if (id != null) vs.setId(id);
        // 自定義屬性直接設組件
        vs.setMin(min); vs.setMax(max); vs.setStep(step);
        // 標準屬性直接傳給子組件
        vs.findComponent("input").getAttributes().put("size", new Integer(size));
    }
    public String getComponentType() { return "xyz.ValueScroller"; }
    public String getRendererType() { return null; } // 無需自定義渲染器
}

TLD 定義

<tag>
  <name>valueScroller</name>
  <tag-class>component.taglib.ValueScrollerTag</tag-class>
  <body-content>JSP</body-content>
  <attribute><name>value</name></attribute>  <!-- ValueBinding -->
  <attribute><name>size</name></attribute>   <!-- 轉發給 UIInput -->
  <attribute><name>min</name></attribute>
  <attribute><name>max</name></attribute>
  <attribute><name>step</name></attribute>
</tag>

註冊與使用

<!-- faces-config.xml -->
<component>
  <component-type>xyz.ValueScroller</component-type>
  <component-class>component.ValueScroller</component-class>
</component>
<!-- Test.jsp -->
<%@ taglib uri="/WEB-INF/lib/ValueScroller.tld" prefix="xyz" %>
<xyz:valueScroller value="#{pc_Test.itemCount}" size="5" min="-50" max="10000" step="2"/>

標準渲染器類型速查表

組件族組件類渲染器類型HTML 輸出
InputUIInput / HtmlInputTextjavax.faces.Text<input type="text">
CommandUICommandjavax.faces.Link / Button<a> / <button>
GraphicUIGraphicjavax.faces.Image<img>
PanelUIPaneljavax.faces.Grid / Group<table> / <span>
OutputUIOutputjavax.faces.Text純文本

優勢總結

  • 零 encodeBegin/encodeEnd/decode 代碼。
  • 零 saveState/restoreState 代碼(標準組件自動管理)。
  • 組件類無 HTML 依賴,理論上可渲染 WML/其他標記語言。
  • 屬性透傳機制極簡:標準屬性直達子組件,自定義屬性組件類自行處理。
  • 開發效率提升 5-10 倍,適合快速構建企業級組件庫。
源碼下載:配套源碼
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved