EJB 最佳實踐:動態委派

發表于:2007-06-06來源:作者:點擊數: 標簽:ejb委派最佳
EJB 最佳實踐:動態委派 用 Java 反射構建更通用的業務委派 級別:中級 Brett McLaughlin ( brett@oreilly.com ) 作家兼編輯,O'Reilly and Associates 2003 年 3 月 盡管業務委派類確實給您的企業 Java 設計帶來了激動人心的新靈活性,但為您應用程序中的
EJB 最佳實踐:動態委派
用 Java 反射構建更通用的業務委派

級別:中級

Brett McLaughlinbrett@oreilly.com
作家兼編輯,O'Reilly and Associates
2003 年 3 月

盡管業務委派類確實給您的企業 Java 設計帶來了激動人心的新靈活性,但為您應用程序中的每個會話 bean 都編碼一個業務委派還是太麻煩了。在 EJB 最佳實踐系列的這篇文章里,Brett McLaughlin 向您展示了如何創建業務委派類的更通用的版本:動態委派。

上一篇技巧文章中,我們討論了如何用業務委派類(請不要與業務接口(Business Interface)模式相混淆)來訪問您的 EJB 組件。通過在客戶機代碼和 EJB 代碼之間插入業務委派類,我們可以將應用程序的 Web 層與 EJB 語義和業務邏輯隔離開來。

研究這類設計的一種方法是看它有多通用。先從一個應用程序入手,該應用程序中的業務邏輯和技術函數是緊密地交織在一起的,我們已經逐步分離出應用程序的不同層,并使用不同的技術來降低它們的相互依賴。在這樣做時,您應該會發現:應用程序底層結構越通用,則隨著時間的推移,它就會越健壯且可維護性越好。

在這篇技巧文章中,我們將繼續使用通用設計的思想。我們將從研究當前業務委派實現的限制入手,然后我將向您展示如何通過創建更通用的(因而不那么呆板)業務委派類實現來克服這些限制。

業務委派類:復習
回顧一下我們上個月討論的 Library bean 接口的業務委派類。

LibraryDelegate 類的大部分代碼只是復制了原始 Library bean 的方法。LibraryDelegate 添加了 init()、destroy() 和構造器方法,然后用這些方法將任務委派給 Library bean。在這樣做時,委派充當 Web 層和企業 bean 之間的緩沖區。這里是原始 bean 的業務接口。

方法的繁殖
除非您考慮到多個會話 bean 有 10 個、20 個或更多方法,否則這種方法的問題并不明顯。實際上,找到擁有 50 個或更多方法的會話 bean 并不罕見!因為 bean 的業務接口必須包括該 bean 的所有方法,所以業務委派類也將這樣做。那會使代碼過于龐大,并很容易出錯。

您的輸入是否太快了?
在使用 EJB 組件時,我們經??缭竭h程接口、業務接口、實現類和現在的業務委派類來復制許多方法。我們中的許多人喜歡在編輯器窗口和 IDE 之間剪切和粘貼方法,而不是手工輸入它們,但請注意:按 Option+V 或 Control+V 會象手工輸入方法一樣容易出錯 — 您添加的方法越多,出錯的可能性就越大。通過仔細檢驗您是否正確地輸入了方法,以及是否按預期剪切和粘貼了它們,最終可以使您避免許多麻煩。

除了龐大的代碼之外,我們還必須考慮變化因素。因為 Delegate 類必須復制 bean 的所有方法,并且隨著時間推移,bean 不可避免地會發生變化,您會發現需要花費很多時間來將新的方法添加到 Delegate,更別提重新編譯了,有可能還要測試新代碼。

就其本身而言,這看起來似乎不是很嚴重的問題。但假如我們開始使用業務委派類來從技術基礎結構(在本文是指 EJB 組件)中抽象業務和表示邏輯呢。如果更改遠程接口需要對業務委派進行更改,那么,實際上,我們的業務委派仍然與底層組件聯系在一起。

我們需要更好的方法,您說是不是。確實有更好的方法。

動態委派
解決方案是使用動態委派,而它又使用 Java 反射(reflection)。您可以使委派動態地調用目標 EJB 組件的遠程接口上的方法(通過 Java Reflection API),而不必將每個業務方法硬編碼到委派中。這樣允許徹底消除來自遠程接口的耦合,因為,為 bean 的業務或遠程接口添加方法時不需要在業務委派中進行相應更改。使用動態委派還使得更改您的技術基礎結構更為容易。從遠程接口和 EJB 技術遷移到另一種技術(如 Java Data Objects,JDO)只需要更改委派的 init() 方法。所有其它方法調用將繼續通過 bean 的接口進行委派,并且可以繼續使用無需進一步更改。清單 1 顯示了 Library 業務委派的動態版本:

清單 1. Library bean 的業務委派 clearcase/" target="_blank" >cccccc border=1>

package com.ibm.library;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import javax.ejb.CreateException;
import javax.naming.NamingException;

public class LibraryDelegate implements ILibrary {
  private ILibrary library;
  private Map availableMethods;

  public LibraryDelegate() {
    init();
  }

      public void init() {
        // Look up and obtain our session bean
        try {
          LibraryHome libraryHome =
            (LibraryHome)EJBHomeFactory.getInstance().lookup(
              "java:comp/env/ejb/LibraryHome", LibraryHome.class);
          library = libraryHome.create();

          // Get the methods available for use in proxying
          availableMethods = new HashMap();
          Method[] methods = ILibrary.class.getMethods();
          for (int i=0; i<methods.length; i++) {
            availableMethods.put(methods[i].getName(),
              methods[i]);
          }
        } catch (NamingException e) {
          throw new RuntimeException(e);
        } catch (CreateException e) {
          throw new RuntimeException(e);
        } catch (RemoteException e) {
          throw new RuntimeException(e);
        }
      }

      // All the hard-coded methods are removed
      public Object
        invoke(Object proxy, Method method, Object[] args)
        throws Throwable{

        try {
          // See if this is init() or destroy()
          if (method.getName().equals("init")) {
            init();
            return null;
          } else if (method.getName().equals("destroy")) {
            destroy();
            return null;
          } else {
            Method method =
              (Method)availableMethods.get(method.getName());

            // See if we found anything
            if (method != null) {
              return method.invoke(library, args);
            } else {
              throw new
                NoSuchMethodException("The Library does not " +
                "support the  " + method.getName() +" method.");
            }
          }
        } catch (InvocationTargetException e) {
          // We don't support throwing RuntimeExceptions from EJBs
          // directly
          if (e.getTargetException() instanceof RemoteException) {
            throw new RuntimeException(e);
            } else {
              throw e.getTargetException();
            }
          }
        }

        public void destroy() {
          // In this case, do nothing
        }
      }

動態委派出色地解決了委派、bean 及其業務接口之間的耦合問題。但是,它并不是完美的解決方案,也不會總是最好的解決方案。雖然您從這種方法中獲得了極大的靈活性,但也付出了性能代價。Java 反射并不十分快,因此在調用 invoke() 和獲得結果之間,您會感到一些延時。前一篇技巧文章中展示的靜態業務委派類是更快的解決方案,但它使您的業務層和技術層的耦合程度比您所希望的要高。因此,在權衡這兩個選擇時,選擇哪一個要根據設計或性能而定。當應用程序的設計比整體性能更重要時,動態委派是更好的選擇。當性能是更重要的因素時,業務委派是更好的選擇。

您可能會發現自己正在將動態委派用于內部網應用程序,在內部網中,所有機器都在本地網絡上,并且您會經常添加或更改功能。對于電子商業和面向顧客的應用程序,原始的業務委派可能是更好的選擇。在這兩種情況下,您現在都應該對業務委派及其工作原理有了更好的理解。好好玩,玩得開心點,我們網上再見!

 

原文轉自:http://www.anti-gravitydesign.com

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97