シンプルなサービス指向のJ2EEアプリケーションフレームワークを設計する

今日、開発者は、J2EEプログラミングを支援するオープンソースフレームワーク(Struts、Spring、Hibernate、Tiles、Avalon、WebWorks、Tapestry、またはOracle ADFなど)に溢れています。多くの開発者は、これらのフレームワークが問題の万能薬ではないことに気づいています。オープンソースであるからといって、変更や改善が容易であるとは限りません。フレームワークが重要な領域で不足している場合、特定のドメインのみに対応している場合、または肥大化して高すぎる場合は、その上に独自のフレームワークを構築する必要があります。Strutsのようなフレームワークを構築することは簡単な作業ではありません。ただし、Strutsやその他のフレームワークを活用するフレームワークを段階的に開発する必要はありません。

この記事では、X18p(Xiangnong 18 Palm、伝説的な強力なカンフーファイターにちなんで名付けられました)の開発方法を紹介します。これは、ほとんどのJ2EEフレームワークで無視される2つの一般的な問題である密結合と肥大化したDAO(データアクセスオブジェクト)に対処するサンプルフレームワークです。コード。後で説明するように、X18pは、Struts、Spring、Axis、Hibernate、およびその他のフレームワークをさまざまなレイヤーで活用します。うまくいけば、同様の手順で、独自のフレームワークを簡単にロールし、プロジェクトからプロジェクトへと成長させることができます。

このフレームワークの開発で私が採用するアプローチは、IBMのRational Unified Process(RUP)の概念を使用しています。私はこれらの手順に従います:

  1. 最初に簡単な目標を設定する
  2. 既存のJ2EEアプリケーションアーキテクチャを分析し、問題を特定します
  3. 代替フレームワークを比較し、構築が最も簡単なフレームワークを選択します
  4. コードを段階的に開発し、頻繁にリファクタリングする
  5. フレームワークのエンドユーザーと会い、定期的にフィードバックを収集します
  6. テスト、テスト、テスト

ステップ1.簡単な目標を設定する

野心的な目標を設定し、すべての問題を解決する最先端のフレームワークを実装することは魅力的です。十分なリソースがある場合、それは悪い考えではありません。一般に、プロジェクトのフレームワークを事前に開発することは、具体的なビジネス価値を提供できないオーバーヘッドと見なされます。小さく始めることで、予期しないリスクを軽減し、開発時間を短縮し、学習曲線を短縮し、プロジェクトの利害関係者の賛同を得ることができます。X18pの場合、J2EEコードとの過去の遭遇に基づいて、2つの目標のみを設定しました。

  1. J2EEActionコードの結合を減らす
  2. J2EEDAOレイヤーでのコードの繰り返しを減らす

全体として、生産性を向上させることで、より高品質のコードを提供し、開発と保守の総コストを削減したいと考えています。それで、これらの目標を達成するために、ステップ2から6を2回繰り返します。

コード結合を減らす

ステップ2.以前のJ2EEアプリケーションアーキテクチャを分析する

J2EEアプリケーションフレームワークが導入されている場合は、最初にそれをどのように改善できるかを確認する必要があります。明らかに、最初から始めるのは意味がありません。X18pの場合、図1に示す典型的なJ2EEStrutsアプリケーションの例を見てみましょう。

Actionを呼び出しXXXManager、sをXXXManager呼び出しますXXXDAO。Strutsを組み込んだ典型的なJ2EEデザインには、次のアイテムがあります。

  • HttpServletまたはStrutsのActionそのハンドル層HttpRequestHttpResponse
  • ビジネスロジック層
  • データアクセス層
  • ドメインエンティティにマップするドメインレイヤー

上記のアーキテクチャの何が問題になっていますか?答え:密結合。のロジックActionが単純な場合、アーキテクチャは問題なく機能します。しかし、多くのEJB(Enterprise JavaBeans)コンポーネントにアクセスする必要がある場合はどうでしょうか。さまざまなソースからWebサービスにアクセスする必要がある場合はどうなりますか? JMX(Java Management Extensions)にアクセスする必要がある場合はどうなりますか? Strutsには、struts-config.xmlファイルからこれらのリソースを検索するのに役立つツールがありますか?答えはいいえだ。 Strutsは、Web層のみのフレームワークであることが意図されています。Actionをさまざまなクライアントとしてコーディングし、ServiceLocatorパターンを介してバックエンドを呼び出すことができます。ただし、そうすると、Actionexecute()メソッドに2つの異なるタイプのコードが混在します。

最初のタイプのコードは、Web層HttpRequest/に関連していますHttpResponse。たとえば、コードはActionFormまたはからHTTPフォームデータを取得しますHttpRequest。 HTTPリクエストまたはHTTPセッションにデータを設定し、それをJSP(JavaServer Pages)ページに転送して表示するコードもあります。

ただし、2番目のコードタイプはビジネス層に関連しています。ではActionEJBObjectJMS(Java Message Service)トピック、さらにはJDBC(Java Database Connectivity)データソースなどのバックエンドコードを呼び出し、JDBCデータソースから結果データを取得します。でServiceLocatorパターンを使用Actionして、ルックアップを行うことができます。ActionローカルPOJO(プレーンオールドJavaオブジェクト)のみを参照することもできますxxxManager。それでも、バックエンドオブジェクトまたはxxxManagerのメソッドレベルのシグネチャはに公開されActionます。

それがどのようにAction機能するのですか?の性質Actionは、HTMLからデータを取り込み、HTTPリクエスト/セッションを使用してデータをHTMLに設定する方法を考慮することになっているサーブレットです。また、ビジネスロジック層とインターフェイスして、その層からデータを取得または更新しますが、どのような形式またはプロトコルであってActionも、それほど気にする必要はありません。

ご想像のとおり、Strutsアプリケーションが大きくなると、Actions(Web層)とビジネスマネージャー(ビジネス層)の間で緊密な参照が発生する可能性があります(図1の赤い線と矢印を参照)。

この問題を解決するために、市場のオープンフレームワークを検討することができます。影響を与える前に、それらに私たち自身の思考を刺激させてください。SpringFrameworkがレーダー画面に表示されます。

ステップ3.代替フレームワークを比較する

Spring Frameworkのコアは、と呼ばれる概念BeanFactoryです。これは、優れたルックアップファクトリの実装です。これは、以前は注入依存性と呼ばれていた制御の反転(IoC)機能を備えているという点で、サービスロケーターパターンとは異なります。アイデアは、ApplicationContextgetBean()メソッドを呼び出してオブジェクトを取得することです。このメソッドは、Spring構成ファイルでオブジェクト定義を検索し、オブジェクトを作成して、オブジェクトを返しjava.lang.Objectます。getBean()オブジェクトのルックアップに適しています。ApplicationContextで参照する必要があるオブジェクト参照は1つだけのようActionです。ただし、で直接使用する場合はそうではありません。これはActiongetBean()の戻りオブジェクトタイプをEJB / JMX / JMS / Webサービスクライアントにキャストする必要があるためです。Actionそれでも、メソッドレベルでバックエンドオブジェクトを認識している必要があります。緊密な結合はまだ存在します。

オブジェクトメソッドレベルの参照を避けたい場合、他に何を使用できますか?当然、サービスが思い浮かびます。サービスはユビキタスですが中立的な概念です。いわゆるWebサービスだけでなく、何でもサービスにすることができます。ActionステートレスセッションBeanのメソッドをサービスとして扱うこともできます。また、JMSトピックの呼び出しをサービスの消費として扱うこともできます。サービスを利用するように設計する方法は、非常に一般的です。

上記の分析と比較から戦略を策定し、危険を発見し、リスクを軽減することで、創造性を刺激し、サービス指向の概念を示すために薄いサービスブローカーレイヤーを追加できます。

ステップ4.開発とリファクタリング

サービス指向の概念思考をコードに実装するには、次のことを考慮する必要があります。

  • サービスブローカー層は、Web層とビジネス層の間に追加されます。
  • 概念的には、Actionはビジネスサービス要求のみを呼び出し、サービスルーターに要求を渡します。サービスルーターは、サービスマッピングXMLファイルを検索することにより、ビジネスサービス要求をさまざまなサービスプロバイダーコントローラーまたはアダプターに接続する方法を知っていますX18p-config.xml
  • サービスプロバイダーコントローラーは、基盤となるビジネスサービスを見つけて呼び出すための特定の知識を持っています。ここで、ビジネスサービスは、POJO、LDAP(ライトウェイトディレクトリアクセスプロトコル)、EJB、JMX、COM、およびWebサービスからCOTS(市販)製品APIまで何でもかまいません。X18p-config.xmlサービスプロバイダーコントローラーがジョブを実行するのに役立つ十分なデータを提供する必要があります。
  • X18pの内部オブジェクトルックアップと参照にSpringを活用します。
  • サービスプロバイダーコントローラーを段階的に構築します。ご覧のとおり、実装されているサービスプロバイダーコントローラーが多いほど、X18pの統合能力は高くなります。
  • Strutsなどの既存の知識を保護しますが、新しいものが登場するのを待ちます。

ここでAction、サービス指向X18pフレームワークを適用する前後のコードを比較します。

X18pなしのStrutsアクション

public ActionForward execute(ActionMapping Mapping、ActionForm form、HttpServletRequest request、HttpServletResponse response)throws IOException、ServletException {... UserManager userManager = new UserManager(); String userIDRetured = userManager.addUser( "John Smith")...}

X18pでのStrutsアクション

public ActionForward execute(ActionMapping Mapping、ActionForm form、HttpServletRequest request、HttpServletResponse response)throws IOException、ServletException {... ServiceRequest bsr = this.getApplicationContext()。getBean( "businessServiceRequest"); bsr.setServiceName( "ユーザーサービス"); bsr.setOperation( "addUser"); bsr.addRequestInput( "param1"、 "addUser"); 文字列userIDRetured =(文字列)bsr.service(); ...}

Spring supports lookups to the business service request and other objects, including POJO managers, if any.

Figure 2 shows how the Spring configuration file, applicationContext.xml, supports the lookup of businessServiceRequest and serviceRouter.

In ServiceRequest.java, the service() method simply calls Spring to find the service router and passes itself to the router:

 public Object service() { return ((ServiceRouter) this.serviceContext.getBean("service router")).route(this); } 

The service router in X18p routes user services to the business logic layer with X18p-config.xml's help. The key point is that the Action code doesn't need to know where or how user services are implemented. It only needs to be aware of the rules for consuming the service, such as pushing the parameters in the correct order and casting the right return type.

Figure 3 shows the segment of X18p-config.xml that provides the service mapping information, which ServiceRouter will look up in X18p.

For user services, the service type is POJO. ServiceRouter creates a POJO service provider controller to handle the service request. This POJO's springObjectId is userServiceManager. The POJO service provider controller uses Spring to look up this POJO with springObjectId. Since userServiceManager points to class type X18p.framework.UserPOJOManager, the UserPOJOManager class is the application-specific logic code.

Examine ServiceRouter.java:

 public Object route(ServiceRequest serviceRequest) throws Exception { // /1. Read all the mapping from XML file or retrieve it from Factory // Config config = xxxx; // 2. Get service's type from config. String businessServiceType = Config.getBusinessServiceType(serviceRequest.getServiceName()); // 3. Select the corresponding Router/Handler/Controller to deal with it. if (businessServiceType.equalsIgnoreCase("LOCAL-POJO")) { POJOController pojoController = (POJOController) Config.getBean("POJOController"); pojoController.process(serviceRequest); } else if (businessServiceType.equalsIgnoreCase("WebServices")) { String endpoint = Config.getWebServiceEndpoint(serviceRequest.getServiceName()); WebServicesController ws = (WebServicesController) Config.getBean("WebServicesController"); ws.setEndpointUrl(endpoint); ws.process(serviceRequest); } else if (businessServiceType.equalsIgnoreCase("EJB")) { EJBController ejbController = (EJBController) Config.getBean("EJBController"); ejbController.process(serviceRequest); } else { //TODO System.out.println("Unknown types, it's up to you how to handle it in the framework"); } // That's it, it is your framework, you can add any new ServiceProvider for your next project. return null; } 

The above routing if-else block could be refactored into a Command pattern. The Config object provides the Spring and X18p XML configuration lookup. As long as valid data can be retrieved, it's up to you how to implement the lookup mechanism.

Assuming a POJO manager, TestPOJOBusinessManager, is implemented, the POJO service provider controller (POJOServiceController.java) then looks for the addUser() method from the TestPOJOBusinessManager and invokes it with reflection (see the code available from Resources).

By introducing three classes (BusinessServiceRequester, ServiceRouter, and ServiceProviderController) plus one XML configuration file, we have a service-oriented framework as a proof-of-concept. Here Action has no knowledge regarding how a service is implemented. It cares about only input and output.

さまざまなAPIとプログラミングモデルを使用してさまざまなサービスプロバイダーを統合することの複雑さは、Web層で作業するStruts開発者から保護されています。X18p-config.xmlがサービス契約として事前に設計されている場合、Strutsとバックエンド開発者は契約によって同時に作業できます。

図4は、アーキテクチャの新しい外観を示しています。

一般的なサービスプロバイダーのコントローラーと実装戦略を表1にまとめました。簡単に追加できます。

表1.一般的なサービスプロバイダーコントローラーの実装戦略