熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Javascript >> 正文

將Flex集成到Java EE應用程序的最佳實踐

2013-11-23 17:56:54  來源: Javascript 

  傳統的 Java EE 應用程序通常使用某種 MVC 框架(例如Struts)作為前端用戶界面隨著 Flex 的興起基於 RIA 的客戶端能夠給用戶帶來更酷的界面更短的響應時間以及更接近於桌面應用程序的體驗本文將講述如何將 Flex 集成至一個現有的 Java EE 應用程序中以及如何應用最佳實踐高效率地並行開發 Java EE 和 Flex

  開發環境

  本文的開發環境為 Windows UltimateEclipse Flex Builder (從 參考資源 獲得下載鏈接)Java EE 服務器使用 Resin 當然您也可以使用 Tomcat 等其他 Java EE 服務器

  現有的 Java EE 應用

  假定我們已經擁有了一個管理雇員信息的 Java EE 應用名為 EmployeeMgmtServer結構如 圖 所示

  圖 Java EE 工程結構

  圖 1. Java EE 工程結構

  這是一個典型的 Java EE 應用使用了流行的 Spring 框架為了簡化數據庫操作我們使用了內存數據庫 HSQLDB對這個簡單的應用省略了 DAO直接在 Fa?ade 中通過 Spring 的 JdbcTemplate 操作數據庫最後EmployeeMgmt 應用通過 Servlet 和 JSP 頁面為用戶提供前端界面

  圖 EmployeeMgmt Web 界面

  圖 2. EmployeeMgmt Web 界面

  該界面為傳統的 HTML 頁面用戶每次點擊某個鏈接都需要刷新頁面由於 Employee Management 系統更接近於傳統的桌面應用程序因此用 Flex 重新編寫界面會帶來更好的用戶體驗

  集成 BlazeDS

  如何將 Flex 集成至該 Java EE 應用呢?現在我們希望用 Flex 替換掉原有的 Servlet 和 JSP 頁面就需要讓 Flex 和 Java EE 後端通信Flex 支持多種遠程調用方式包括 HTTPWeb Services 和 AMF不過針對 Java EE 開發的服務器端應用可以通過集成 BlazeDS充分利用 AMF 協議並能輕易與 Flex 前端交換數據這種方式是 Java EE 應用程序集成 Flex 的首選

  BlazeDS 是 Adobe LifeCycle Data Services 的開源版本遵循 LGPL v 授權可以免費使用BlazeDS 為 Flex 提供了基於 AMF 二進制協議的遠程調用支持其作用相當於 Java 的 RMI有了 BlazeDS通過簡單的配置一個 Java 接口就可以作為服務暴露給 Flex供其遠程調用

  盡管現有的 EmployeeMgmt 應用程序已經有了 Fa?ade 接口但這個接口是暴露給 Servlet 使用的最好能再為 Flex 定義另一個接口 FlexService並隱藏 Java 語言的特定對象(如 清單 所示)

  清單 FlexService interface

   public interface FlexService {
    Employee createEmployee(String name String title boolean gender Date birth);
    void deleteEmployee(String id);
    Employee[] queryByName(String name);
    Employee[] queryAll();
}

  現在Java EE 後端與 Flex 前端的接口已經定義好了要完成 Java EE 後端的接口實現類非常容易利用 Spring 強大的依賴注入功能可以通過幾行簡單的代碼完成

  清單 FlexServiceImpl class

   public class FlexServiceImpl implements FlexService {
    private static final Employee[] EMPTY_EMPLOYEE_ARRAY = new Employee[];
    private Facade facade;

    public void setFacade(Facade facade) {
        thisfacade = facade;
    }

    public Employee createEmployee(String name String title boolean gender
        Date birth) {
        return facadecreateEmployee(name title gender birth);
    }

    public void deleteEmployee(String id) {
        facadedeleteEmployee(id);
    }

    public Employee[] queryAll() {
        return facadequeryAll()toArray(EMPTY_EMPLOYEE_ARRAY);
    }

    public Employee[] queryByName(String name) {
        return facadequeryByName(name)toArray(EMPTY_EMPLOYEE_ARRAY);
    }
}

  然後我們將 BlazeDS 所需的 jar 包放至 /WEBINF/lib/BlazeDS 需要如下的 jar

  清單 BlazeDS 依賴的 Jar

   backportutilconcurrentjar
commons
commonsloggingjar
flexmessagingcommonjar
flexmessagingcorejar
flexmessagingproxyjar
flexmessagingremotingjar

  在 webxml 中添加 HttpFlexSession 和 Servlet 映射HttpFlexSession 是 BlazeDS 提供的一個 Listener負責監聽 Flex 遠程調用請求並進行一些初始化設置

  清單 定義 Flex Listener

   <listener>
    <listenerclass>ssagingHttpFlexSession</listenerclass>
</listener>

  MessageBrokerServlet 是真正處理 Flex 遠程調用請求的 Servlet我們需要將其映射到指定的 URL

  清單 定義 Flex servlet

   <servlet>
    <servletname>messageBroker</servletname>
    <servletclass>ssagingMessageBrokerServlet</servletclass>
    <initparam>
        <paramname>nfigurationfile</paramname>
        <paramvalue>/WEBINF/flex/servicesconfigxml</paramvalue>
    </initparam>
    <loadonstartup></loadonstartup>
</servlet>

<servletmapping>
    <servletname>messageBroker</servletname>
    <urlpattern>/messagebroker/*</urlpattern>
</servletmapping>

  BlazeDS 所需的所有配置文件均放在 /WEBINF/flex/ 目錄下BlazeDS 將讀取 servicesconfigxml 配置文件該配置文件又引用了 remotingconfigxmlproxyconfigxml 和 messagingconfigxml 這 個配置文件所以一共需要 個配置文件

  由於 BlazeDS 需要將 Java 接口 FlexService 暴露給 Flex 前端因此我們在配置文件 remotingconfigxml 中將 FlexService 接口聲明為一個服務

  清單 定義 flexService 服務

   <destination id=flexService>
    <properties>
        <source>orgexpressmeemployeemgmtflexFlexServiceImpl</source>
        <scope>application</scope>
    </properties>
</destination>

  服務名稱通過 destination 的 id 屬性指定Flex 前端通過該服務名稱來進行遠程調用scope 指定為 application表示該對象是一個全局對象

  然而按照默認的聲明BlazeDS 會去實例化 FlexService 對象對於一個 Java EE 應用來說通常這些服務對象都是被容器管理的(例如Spring 容器或 EJB 容器)更合適的方法是查找該服務對象而非直接實例化因此需要告訴 BlazeDS 通過 Factory 來查找指定的 FlexService 對象修改配置如下

  清單 通過 factory 定義 flexService

   <destination id=flexService>
    <properties>
        <factory>flexFactory</factory>
        <source>flexService</source>
        <scope>application</scope>
    </properties>
</destination>

  現在Flex 如何才能通過 BlazeDS 調用 FlexService 接口呢?由於 FlexService 對象已經被 Spring 管理因此我們需要編寫一個 FlexFactory 告訴 BlazeDS 如何找到 Spring 管理的 FlexService 的實例flexFactory 在 servicesconfigxml 中指定

  清單 定義 flexFactory

   <factories>
    <factory id=flexFactory class=orgexpressmeemployeemgmtflexFlexFactoryImpl/>
</factories>

  FlexFactoryImpl 實現了 FlexFactory 接口該接口完成兩件事情

  創建 FactoryInstance 對象

  通過 FactoryInstance 對象查找我們需要的 FlexService

  因此需要一個 FactoryInstance 的實現類我們編寫一個 SpringFactoryInstance以便從 Spring 的容器中查找 FlexService

  清單 SpringFactoryInstance class

   class SpringFactoryInstance extends FactoryInstance {
    private Log log = LogFactorygetLog(getClass());

    SpringFactoryInstance(FlexFactory factory String id ConfigMap properties) {
        super(factory id properties);
    }

    public Object lookup() {
        ApplicationContext appContext = WebApplicationContextUtils
                getRequiredWebApplicationContext(
                    FlexContextgetServletConfig()getServletContext()
        );
        String beanName = getSource();
        try {
            (Lookup bean from Spring ApplicationContext: + beanName);
            return appContextgetBean(beanName);
        }
        catch (NoSuchBeanDefinitionException nex) {
           
        }
        catch (BeansException bex) {
           
        }
        catch (Exception ex) {
           
        }
    }
}

  FlexFactoryImpl 負責實例化 SpringFactoryInstance 並通過 SpringFactoryInstance 的 lookup() 方法查找 FlexService 接口對象

  清單 FlexFactoryImpl class

   public class FlexFactoryImpl implements FlexFactory {
    private Log log = LogFactorygetLog(getClass());

    public FactoryInstance createFactoryInstance(String id ConfigMap properties) {
        (Create FactoryInstance);
        SpringFactoryInstance instance = new SpringFactoryInstance(this id properties);
        instancesetSource(propertiesgetPropertyAsString(SOURCE instancegetId()));
        return instance;
    }

    public Object lookup(FactoryInstance instanceInfo) {
        (Lookup service object);
        return instanceInfolookup();
    }

    public void initialize(String id ConfigMap configMap) {
    }
}

  以下是 BlazeDS 查找 FlexService 接口的過程

  BlazeDS 將首先創建 FlexFactory 的實例—— FlexFactoryImpl

  當接收到 Flex 前端的遠程調用請求時BlazeDS 通過 FlexFactory 創建 FactoryInstance 對象並傳入請求的 Service ID在這個應用程序中被創建的 FactoryInstance 實際對象是 SpringFactoryInstance

  FactoryInstance 的 lookup() 方法被調用在 SpringFactoryInstance 中首先查找 Spring 容器然後通過 Bean 的 ID 查找 Bean最終FlexService 接口的實例被返回

  注意到 destination 的 id 並沒有寫死在代碼中而是通過以下語句獲得的

  清單 獲取 destination 的 ID

   propertiesgetPropertyAsString(SOURCE instancegetId())

  Property 的 SOURCE 屬性由 BlazeDS 讀取 XML 配置文件獲得

  清單 配置 destination 的 id

   <destination id=flexService>
    <properties>
        <factory>flexFactory</factory>
        <source>flexService</source>
        <scope>application</scope>
    </properties>
</destination>

  如果您沒有使用 Spring 框架也不要緊只需修改 FactoryInstance 的 lookup() 方法例如對於一個 EJB 來說lookup() 方法應該通過 JNDI 查找返回遠程接口無論應用程序結構如何我們的最終目標是向 BlazeDS 返回一個 FlexService 的實例對象

  開發 Flex 客戶端

  首先安裝 Flex Builder 可以在 Adobe 的官方網站獲得 天免費試用版然後打開 Flex Builder 創建一個新的 Flex Project命名為 EmployeeMgmtFlex

  圖 新建 Flex 工程 第一步

  圖 3. 新建 Flex 工程 - 第一步

  Flex Project 需要指定 Server 端的配置文件地址

  圖 新建 Flex 工程 第二步

  圖 4. 新建 Flex 工程 - 第二步

  因此需要填入 EmployeeMgmtServer 項目的 web 根目錄該目錄下必須要存在 /WEBINF/flex/點擊Validate Configuration驗證配置文件是否正確只有通過驗證後才能繼續默認地Flex Builder 將會把生成的 Flash 文件放到 EmployeeMgmtServer 項目的 web/EmployeeMgmtFlexdebug 目錄下

  一個 Flex Project 的目錄結構如下

  圖 Flex 工程的目錄結構

  圖 5. Flex 工程的目錄結構

  用 Flex Builder 做出漂亮的用戶界面非常容易Flex Builder 提供了一個可視化的編輯器通過簡單的拖拽一個毫無經驗的開發人員也能夠設計出漂亮的布局如果熟悉一點 XML 的知識編輯 MXML 也並非難事我們設計的 Employee Management 系統界面的最終效果如下

  圖 用 Flex Builder 的可視化編輯器設計界面

  圖 6. 用 Flex Builder 的可視化編輯器設計界面

  本文不打算討論如何編寫 Flex 界面而是把重點放在如何實現遠程調用

  為了能在 Flex 中實現遠程調用我們需要定義一個 RemoteObject 對象可以通過 ActionScript 編碼創建該對象也可以直接在 MXML 中定義一個 RemoteObject 對象並列出其所有的方法

  清單 定義 flexServiceRO

   <mx:RemoteObject id=flexServiceRO destination=flexService>
    <mx:method name=queryAll result=handleQueryAll(result : ResultEvent)/>
</mx:RemoteObject>

  現在就可以調用這個名為 flexServiceRO 的 RemoteObject 對象的方法了

  清單 調用 FlexServiceROqueryAll()

    flexServiceROqueryAll(function(result : ResultEvent) {
    var employees = resultresult as Array;
});

  運行該 Flex Application雇員信息已經被正確獲取了

  圖 在浏覽器中運行 Flex application

  圖 7. 在浏覽器中運行 Flex application

  增強 RemoteObject 對象

  通過 RemoteObject 進行調用雖然簡單但存在不少問題首先RemoteObject 是一個 Dynamic ClassFlex Builder 的編譯器無法替我們檢查參數類型和參數個數這樣在編寫 ActionScript 代碼時極易出錯此外接口變動時(這種情況常常發生)需要重新修改 RemoteObject 的定義此外Flex 團隊需要一份隨時修訂的完整的 FlexService 接口文檔才能工作

  因此最好能使用強類型的 RemoteObject 接口讓 Flex Builder 的編譯器及早發現錯誤這個強類型的 RemoteObject 最好能通過 Java EE 應用的 FlexService 接口自動生成這樣就無需再維護 RemoteObject 的定義

  為了能完成自動生成 RemoteObject 對象我編寫了一個 JavaActionScript 的 Ant 任務來自動轉換 FlexService 接口以及相關的所有 JavaBeanJavaInterfaceRemoteObjectTask 完成一個 Java 接口對象到 RemoteObject 對象的轉換使用如下的 Ant 腳本

  清單 生成 ActionScript class 的 Ant 腳本

   <taskdef name=genactionscript classname=orgexpressmeantJavaBeanActionScriptTask>
    <classpath refid=buildclasspath />
</taskdef>
<taskdef name=genremoteobject
    classname=orgexpressmeantJavaInterfaceRemoteObjectTask>
    <classpath refid=buildclasspath />
</taskdef>
<genactionscript
    packageName=orgexpressmeemployeemgmt
    includes=Employee
    orderByName=true
    encoding=UTF
    outputDir=${gendir}
/>
<genremoteobject
    interfaceClass=orgexpressmeemployeemgmtflexFlexService
    encoding=UTF
    outputDir=${gendir}
    destination=flexService
/>

  轉換後的 FlexServiceRO 類擁有 Java 接口對應的所有方法每個方法均為強類型簽名並添加額外的兩個可選的函數處理 result 和 fault 事件例如queryByName 方法

  清單 自動生成的 queryByName() 方法

   public function queryByName(arg : String result : Function = null
    fault : Function = null) : void {
    var op : AbstractOperation = rogetOperation(queryByName);
    if (result!=null) {
        opaddEventListener(ResultEventRESULT result);
    }
    if (fault!=null) {
        opaddEventListener(FaultEventFAULT fault);
    }
    var f : Function = function() : void {
        opremoveEventListener(ResultEventRESULT f);
        opremoveEventListener(FaultEventFAULT f);
        if (result!=null) {
            opremoveEventListener(ResultEventRESULT result);
        }
        if (fault!=null) {
            opaddEventListener(FaultEventFAULT fault);
        }
    }
    opaddEventListener(ResultEventRESULT f);
    opaddEventListener(FaultEventFAULT f);
    opsend(arg);
}

  轉換 Java 接口是通過 Interfaceas 和 InterfaceMethodas 兩個模板文件完成的此外所有在 Java EE 後端和 Flex 之間傳遞的 JavaBean 對象也通過 JavaBeanActionScriptTask 自動轉換成對應的 ActionScript 類這是通過 Beanas 模板完成的

  有了 Java 類到 ActionScript 的自動轉換我們在編寫 ActionScript 時就能享受到編譯器檢查和 ActionScript 類方法的自動提示了

  圖 Flex Builder 的代碼自動補全

  圖 8. Flex Builder 的代碼自動補全

  唯一的缺憾是通過反射讀取 FlexService 接口時我們失去了方法的參數名稱因此FlexServiceRO 的方法參數名只能變成 argarg …… 等要讀取 FlexService 接口的方法參數名只能通過解析 Java 源代碼實現

  現在Java EE 後端開發團隊和 Flex 前端開發團隊只需協商定義好 FlexService 接口然後利用 JavaActionScriptFlex 團隊就得到了強類型的 FlexServiceRO 類而 Java EE 團隊則只需集中精力實現 FlexService 接口

  在開發的前期甚至可以用硬編碼的 FlexService 的實現類每當 FlexService 變動時只需再次運行 Ant 腳本就可以獲得最新的 FlexServiceRO 類這樣兩個團隊都可以立刻開始工作僅需要通過 FlexService 接口就可以完美地協同開發

  下載

   描述 名字 大小 下載方法 Java EE 工程源碼 EmployeeMgmtServerzip MB HTTP Flex 工程源碼 EmployeeMgmtFlexzip KB HTTP JavaActionScript 工程源碼 JavaActionScriptzip MB HTTP


From:http://tw.wingwit.com/Article/program/Java/Javascript/201311/25406.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.