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

使用 CMP 2.0 與 XML 處理動態值物件:企業層與表現層資料傳輸新模式

     編輯:Java 相關知識

EJB 2.0 引入本地介面與容器管理關係(CMR)前,實體 EJB 通常建模粗粒度領域物件,主因在於遠端通訊系統開銷阻礙客戶端細粒度存取企業層物件。粗粒度設計透過值物件(Value Object)改善效能——壓縮所有資料在客戶端與企業層間傳遞。但包含大量領域物件的複雜系統,仍導致系統內出現過多值物件,並在企業層與客戶端間形成緊密耦合。EJB 2.0 之前,Bean 提供者須明確提供維護領域物件間關聯的程式碼,領域物件間複雜關係下值物件設計變得極其複雜。

EJB 2.0 容器管理持久化(CMP)與關係欄位(CMR)、本地介面的出現,開啟嶄新途徑。本文示範如何結合 EJB 2.0 強大機制、Bean 組件與 JAXP,動態建立基於資料結構的 XML,在企業層與表現層間傳遞資訊。

XML 資料傳輸的優勢

企業層到客戶端傳遞資料使用 XML,可實現應用中多種鬆耦合;但新增領域物件到實體模型時,可能需新增建立新 DOM 結構的類別。本文開發一個框架,動態遍歷容器管理的關係與本地 EJB 領域,創建可在應用多層間傳遞的 XML 文件。此法優點:

  • 促進企業層與客戶端間鬆耦合
  • 便於管理領域物件間關係
  • 移除系統中複雜值物件——XML 由動態遍歷 CMP 與 CMR 欄位產生,新增領域物件時 Bean 提供者無需建立新 DOM 結構類別

EJB 2.0 本地介面與容器管理關係

EJB 2.0 本地介面促進存取 Bean 組件;結合本地介面可包含與其他容器管理 Bean 的關係。例如服務系統中,UserEJB 可有與 ServiceRequestEJB 一對多雙向關係、與 ProductEJB 一對一單向關聯、與 ServiceRequestHistoryEJB 一對多雙向關係,以及與 PhoneEJB 一對多雙向關係。透過本地介面與容器管理關係,可設計複雜物件圖。

容器管理持久化與關係欄位在 Bean 類別中用抽象存取方法定義。關係欄位存取方法視關係基數返回集合或定義關係的 Bean 本地介面,可透過 Bean 組件本地介面公開。EJB 2.0 詳細規範超出本文範圍,請參閱官方規範。

外觀模式與資料傳輸物件選擇

高效設計模式應透過外觀組件公開使用案例,禁止從客戶端直接存取實體組件。以服務系統為例,「取得指定用戶詳細資料」為一使用案例。外觀組件查找所需用戶實體組件,透過 CMP 與 CMR 欄位取得資料並返回表現層。

資料傳輸物件明顯選擇是簡單 Java Bean:UserBean 含代表持久化與關係欄位的屬性,關係欄位為 java.util.Collection 或其他 Bean 組件。UserBean 可能有 ServiceRequestBean 與 PhoneBean 集合;ServiceRequestBean 可能有 ProductBean 與多個 ServiceRequestHistory Bean。但此選擇主要缺點:使實體模型更複雜、資料傳輸物件 Bean 階層更複雜、在服務層與消費層間創建緊密耦合。

更好選擇:使用 XML DOM 物件作資料傳輸物件。組件產生 org.w3c.dom.Document 物件,表現組件用 JSP 自定義標籤與 XSLT 樣式表「消費」它們。

動態從 CMP 本地 Bean 參考建立 XML

如何從 CMP 本地 Bean 參考建立 XML 文件?可用基於建立不同型 DOM 組件方法的工廠,但會創建過多「工廠」組件,擴展領域模型時可能需新增「工廠」組件。需要的是:通用工具類,接收實體本地物件、導航容器管理關係、動態建立 DOM 結構,處理雙向關係中的循環參考避免無限迴圈、深度導航關係元素。

DOMGenerator 工具類別實作

package ws.business.service.util;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.ejb.EJBLocalObject;
import java.util.Collection;
import java.util.ArrayList;
import java.util.Iterator;
import java.beans.Introspector;
import java.beans.BeanInfo;
import java.beans.PropertyDescriptor;
import java.beans.IntrospectionException;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

public class DOMGenerator {
    private Document doc;
    private int drillDownDepth;
    private ArrayList _circularRef = new ArrayList();
    private int _currentDepth;
    private static String RESERVED = "EJBLocalHome^localHandle^class^primaryKey";

    public DOMGenerator(String docElementName, int drillDownDepth) {
        try {
            DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = fact.newDocumentBuilder();
            doc = builder.newDocument();
        } catch (ParserConfigurationException ex) {
            throw new RuntimeException(ex);
        }
        this.drillDownDepth = drillDownDepth;
        doc.appendChild(doc.createElement(docElementName));
    }

    public Document getDOM(EJBLocalObject local) {
        try {
            populateElement(doc.getDocumentElement(), local);
            return doc;
        } catch (IntrospectionException | IllegalAccessException | InvocationTargetException ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    private void populateElement(Element parent, EJBLocalObject local)
            throws IntrospectionException, IllegalAccessException, InvocationTargetException {
        _circularRef.add(local);
        _currentDepth++;

        BeanInfo info = Introspector.getBeanInfo(local.getClass());
        PropertyDescriptor[] properties = info.getPropertyDescriptors();
        for (int i = 0; i < properties.length; i++) {
            Method propReadMethod = properties[i].getReadMethod();
            String propName = properties[i].getName();
            Object prop = propReadMethod.invoke(local, null);

            if (RESERVED.indexOf(propName) >= 0) continue;

            try {
                EJBLocalObject locProp = (EJBLocalObject) prop;
                if (isCircularRef(locProp) || _currentDepth >= drillDownDepth) continue;
                Element child = doc.createElement(propName);
                populateElement(child, locProp);
                parent.appendChild(child);
            } catch (ClassCastException ex1) {
                try {
                    Collection colProp = (Collection) prop;
                    if (_currentDepth >= drillDownDepth) continue;
                    Element child = doc.createElement(propName);
                    Iterator it = colProp.iterator();
                    while (it.hasNext()) {
                        EJBLocalObject locProp = (EJBLocalObject) it.next();
                        if (isCircularRef(locProp)) continue;
                        Element grandChild = doc.createElement(propName + "-child");
                        child.appendChild(grandChild);
                        populateElement(grandChild, locProp);
                    }
                    parent.appendChild(child);
                } catch (ClassCastException ex2) {
                    parent.setAttribute(propName, prop.toString());
                }
            }
        }
        _circularRef.remove(local);
        _currentDepth--;
    }

    private boolean isCircularRef(EJBLocalObject local) {
        Iterator it = _circularRef.iterator();
        while (it.hasNext())
            if (local.isIdentical((EJBLocalObject) it.next())) return true;
        return false;
    }
}

使用範例與產出 XML

User local = userHome.findByPrimaryKey("1");
return new DOMGenerator("user", 4).getDOM(local);

產生的 DOM 視實體模型而定(範例):

<?xml version="1.0" encoding="UTF-8"?>
<user firstName="Wayne" id="1" lastName="Zheng">
  <requests>
    <requests-child description="Outlook not working" id="2" status="P">
      <product description="Install Laptop" id="1" name="SVC01"/>
    </requests-child>
    <requests-child description="PC not booting" id="1" status="O">
      <histories>
        <histories-child description="Information requested" id="2" loggedAt="2002-4-7 12:12:12.0"/>
        <histories-child description="Request logged" id="1" loggedAt="2000-4-7 00:00:00.0"/>
      </histories>
      <product description="Install Laptop" id="1" name="SVC01"/>
    </requests-child>
  </requests>
  <phones>
    <phones-child id="2" number="0771 8210586" type="M"/>
    <phones-child id="1" number="01908 251575" type="W"/>
  </phones>
</user>

效能、型別安全與模型化考量

表現層可用 XSLT 或 XML JSP 自定義標籤交付企業層產生的 XML 資料至客戶裝置。新增領域模型定義時,僅需定義細粒度本地實體 Bean 與容器管理關係,DOM 產生器提供通用方法產生動態 XML 文件。

但此法有缺點:

  1. 效能疑慮:開發者常擔心 XML 值物件效能。測試環境(1GHz CPU、256MB RAM、Windows ME、WLS 6.1、資料庫同機)處理 1000 筆記錄集與多重關係僅耗 0.5 秒內。
  2. 型別安全:XML 提供最高層級資料抽象,屬性節點值與文字節點皆視為字串。若需在從企業層取回的值物件上進一步業務操作,Java Bean 組件較佳。XML Schema 提供強化 XML 文件型別安全的強大機制。兩種情況皆使用本地實體物件作領域物件。
  3. 缺乏模型化標準:最大缺點是無明確方法模型化基於 XML 資料的領域模型。未見以 UML 註釋或通用規範定義模型化使用 XML 的領域物件關係。

結語

本文詳細研究在 J2EE 應用企業層與表現層間傳遞複雜資料的模型。透過動態遍歷 CMP/CMR 建立 XML,消除手工維護值物件的負擔,實現層間鬆耦合。雖有效能、型別安全、模型化標準等考量,但在需頻繁變更領域模型、追求快速開發與層間解耦的場景,此模式極具吸引力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved